From 48f71b9df197e27cacde5d78a54b73e931caa7cb Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Tue, 12 May 2026 23:14:31 +0200 Subject: [PATCH] chore: update .gitignore and enhance backend and mobile app functionality - Added mobile app build directories and configuration files to .gitignore for cleaner repository management. - Improved error handling in diaryMemberController by requiring diaryDateId and memberId query parameters. - Refactored DiaryMemberService to log tag IDs instead of raw values for better debugging. - Enhanced TournamentParticipantsTab and TournamentTab components with improved touch-action properties for better user experience. - Updated mobile app's gradle.properties and build.gradle.kts for compatibility with AGP 9.x and Kotlin 2.1.21, including new dependencies for Coil and UCrop. - Refactored MainApplication to simplify initialization and improved MainActivity to handle dependencies more robustly. - Updated various UI components in the mobile app to enhance layout and functionality, including MemberDetailScreen and MemberEditScreen. --- .gitignore | 8 + backend/controllers/diaryMemberController.js | 8 +- backend/services/diaryMemberService.js | 2 +- docs/MOBILE_APP_KMP_PLAN.md | 106 + .../tournament/TournamentParticipantsTab.vue | 9 +- frontend/src/views/TournamentTab.vue | 34 +- mobile-app/DEVELOPMENT.md | 81 + mobile-app/README.md | 16 + mobile-app/TODO.md | 212 + mobile-app/composeApp/build.gradle.kts | 21 +- .../src/androidMain/AndroidManifest.xml | 18 +- .../de/tt_tagebuch/app/AppDependencies.kt | 116 + .../kotlin/de/tt_tagebuch/app/MainActivity.kt | 26 +- .../de/tt_tagebuch/app/MainApplication.kt | 16 +- .../tt_tagebuch/app/pdf/DiaryPdfExporter.kt | 191 + .../de/tt_tagebuch/app/pdf/DiaryPdfShare.kt | 22 + .../kotlin/de/tt_tagebuch/app/ui/AppRoot.kt | 4465 ++ .../app/ui/AuthenticatedAsyncImage.kt | 35 + .../tt_tagebuch/app/ui/MemberPortraitCrop.kt | 92 + .../kotlin/de/tt_tagebuch/app/ui/TtTheme.kt | 28 + .../src/androidMain/res/xml/file_paths.xml | 4 + .../res/xml/network_security_config.xml | 9 + .../de/tt_tagebuch/app/ui/DiaryScreen.kt | 2 + .../de/tt_tagebuch/app/ui/HomeScreen.kt | 5 +- .../tt_tagebuch/app/ui/MemberDetailScreen.kt | 5 +- .../de/tt_tagebuch/app/ui/MemberEditScreen.kt | 2 +- .../de/tt_tagebuch/app/ui/MemberScreen.kt | 2 + mobile-app/gradle.properties | 10 + .../gradle/gradle-daemon-jvm.properties | 13 + mobile-app/gradle/libs.versions.toml | 19 +- mobile-app/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 45457 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + mobile-app/gradlew | 15 + mobile-app/gradlew.bat | 12 + mobile-app/iosApp/README.md | 20 + mobile-app/scripts/install-debug-emulator.sh | 9 + mobile-app/settings.gradle.kts | 4 + mobile-app/shared/build.gradle.kts | 22 +- .../http/AndroidHttpClientEngineFactory.kt | 9 + .../shared/state/AndroidClubStorage.kt | 38 + .../shared/state/AndroidLanguageStorage.kt | 24 + .../shared/state/AndroidTokenStorage.kt | 49 + .../de/tt_tagebuch/shared/api/AccidentApi.kt | 23 + .../de/tt_tagebuch/shared/api/ApiConfig.kt | 18 + .../de/tt_tagebuch/shared/api/AuthApi.kt | 23 + .../de/tt_tagebuch/shared/api/ClubsApi.kt | 35 + .../de/tt_tagebuch/shared/api/DiaryApi.kt | 133 + .../shared/api/DiaryMemberActivitiesApi.kt | 28 + .../tt_tagebuch/shared/api/DiaryMemberApi.kt | 56 + .../de/tt_tagebuch/shared/api/GroupApi.kt | 42 + .../shared/api/MemberActivitiesApi.kt | 24 + .../shared/api/MemberGroupPhotosApi.kt | 51 + .../de/tt_tagebuch/shared/api/MembersApi.kt | 54 + .../tt_tagebuch/shared/api/ParticipantsApi.kt | 44 + .../tt_tagebuch/shared/api/PermissionsApi.kt | 15 + .../shared/api/PredefinedActivitiesApi.kt | 28 + .../tt_tagebuch/shared/api/PublicAuthApi.kt | 37 + .../de/tt_tagebuch/shared/api/SessionApi.kt | 15 + .../shared/api/TrainingGroupsApi.kt | 34 + .../shared/api/TrainingStatsApi.kt | 15 + .../shared/api/TrainingTimesApi.kt | 14 + .../shared/api/http/ApiErrorMessage.kt | 35 + .../shared/api/http/ApiException.kt | 7 + .../shared/api/http/AuthedHttpClient.kt | 61 + .../api/http/HttpClientEngineFactory.kt | 8 + .../shared/api/http/PublicHttpClient.kt | 50 + .../shared/api/models/AccidentDtos.kt | 23 + .../de/tt_tagebuch/shared/api/models/Club.kt | 14 + .../api/models/ClubPermissionHelpers.kt | 32 + .../shared/api/models/DiaryActivityExtras.kt | 30 + .../shared/api/models/DiaryDate.kt | 53 + .../api/models/DiaryDateActivityItem.kt | 55 + .../shared/api/models/DiaryImagePaths.kt | 23 + .../shared/api/models/DiaryMemberDtos.kt | 51 + .../shared/api/models/DiaryPlanDtos.kt | 80 + .../api/models/DiaryTrainingParticipant.kt | 30 + .../shared/api/models/LoginRequest.kt | 10 + .../shared/api/models/LoginResponse.kt | 9 + .../tt_tagebuch/shared/api/models/Member.kt | 38 + .../shared/api/models/MemberActivityDtos.kt | 20 + .../shared/api/models/MemberContactDto.kt | 14 + .../shared/api/models/MemberGroupPhotoDtos.kt | 19 + .../shared/api/models/MemberImageDto.kt | 12 + .../shared/api/models/MemberSetBody.kt | 39 + .../shared/api/models/MemberSetMappers.kt | 49 + .../api/models/ParticipantStatusRequest.kt | 8 + .../api/models/PredefinedActivityDtos.kt | 22 + .../shared/api/models/PublicAuthDtos.kt | 25 + .../api/models/SessionStatusResponse.kt | 10 + .../shared/api/models/TrainingGroupDtos.kt | 24 + .../shared/api/models/TrainingStats.kt | 101 + .../shared/api/models/UserClubPermissions.kt | 12 + .../tt_tagebuch/shared/i18n/MobileStrings.kt | 35912 ++++++++++++++++ .../de/tt_tagebuch/shared/i18n/README.md | 13 + .../tt_tagebuch/shared/state/AuthManager.kt | 63 + .../de/tt_tagebuch/shared/state/AuthState.kt | 10 + .../de/tt_tagebuch/shared/state/AuthTokens.kt | 7 + .../tt_tagebuch/shared/state/ClubManager.kt | 90 + .../de/tt_tagebuch/shared/state/ClubState.kt | 13 + .../tt_tagebuch/shared/state/ClubStorage.kt | 7 + .../tt_tagebuch/shared/state/DiaryManager.kt | 303 + .../de/tt_tagebuch/shared/state/DiaryState.kt | 10 + .../tt_tagebuch/shared/state/ErrorMapper.kt | 27 + .../shared/state/LanguageManager.kt | 27 + .../tt_tagebuch/shared/state/LanguageState.kt | 7 + .../shared/state/LanguageStorage.kt | 6 + .../shared/state/MembersManager.kt | 69 + .../tt_tagebuch/shared/state/MembersState.kt | 10 + .../shared/state/MutableTokenProvider.kt | 6 + .../tt_tagebuch/shared/state/TokenProvider.kt | 6 + .../tt_tagebuch/shared/state/TokenStorage.kt | 8 + .../shared/state/TrainingStatsManager.kt | 26 + .../shared/state/TrainingStatsState.kt | 10 + .../api/http/IosHttpClientEngineFactory.kt | 9 + .../shared/state/IosClubStorage.kt | 86 + .../shared/state/IosTokenStorage.kt | 94 + mobile-app_legacy_rn_expo/.gitignore | 41 + mobile-app_legacy_rn_expo/App.tsx | 52 + mobile-app_legacy_rn_expo/README.md | 18 + mobile-app_legacy_rn_expo/app.json | 34 + .../assets/adaptive-icon.png | Bin 0 -> 17547 bytes mobile-app_legacy_rn_expo/assets/favicon.png | Bin 0 -> 1466 bytes mobile-app_legacy_rn_expo/assets/icon.png | Bin 0 -> 22380 bytes .../assets/splash-icon.png | Bin 0 -> 17547 bytes mobile-app_legacy_rn_expo/index.ts | 8 + mobile-app_legacy_rn_expo/package-lock.json | 9524 ++++ mobile-app_legacy_rn_expo/package.json | 34 + .../src/api/apiClient.ts | 28 + mobile-app_legacy_rn_expo/src/config/env.ts | 17 + mobile-app_legacy_rn_expo/src/i18n/index.ts | 30 + .../src/navigation/AppNavigation.tsx | 43 + .../src/screens/HomeScreen.tsx | 29 + .../src/screens/LoginScreen.tsx | 64 + .../src/state/auth/AuthContext.tsx | 84 + .../src/state/auth/authStore.ts | 25 + .../src/state/auth/storage.ts | 27 + mobile-app_legacy_rn_expo/tsconfig.json | 6 + scripts/generate-mobile-i18n.js | 222 + 138 files changed, 54488 insertions(+), 56 deletions(-) create mode 100644 docs/MOBILE_APP_KMP_PLAN.md create mode 100644 mobile-app/DEVELOPMENT.md create mode 100644 mobile-app/README.md create mode 100644 mobile-app/TODO.md create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/AppDependencies.kt create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfExporter.kt create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfShare.kt create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AppRoot.kt create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AuthenticatedAsyncImage.kt create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/MemberPortraitCrop.kt create mode 100644 mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/TtTheme.kt create mode 100644 mobile-app/composeApp/src/androidMain/res/xml/file_paths.xml create mode 100644 mobile-app/composeApp/src/androidMain/res/xml/network_security_config.xml create mode 100644 mobile-app/gradle/gradle-daemon-jvm.properties create mode 100644 mobile-app/gradle/wrapper/gradle-wrapper.jar create mode 100644 mobile-app/gradle/wrapper/gradle-wrapper.properties create mode 100755 mobile-app/gradlew create mode 100644 mobile-app/gradlew.bat create mode 100644 mobile-app/iosApp/README.md create mode 100755 mobile-app/scripts/install-debug-emulator.sh create mode 100644 mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/api/http/AndroidHttpClientEngineFactory.kt create mode 100644 mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidClubStorage.kt create mode 100644 mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidLanguageStorage.kt create mode 100644 mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidTokenStorage.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AccidentApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ApiConfig.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AuthApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ClubsApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberActivitiesApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/GroupApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberActivitiesApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberGroupPhotosApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MembersApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ParticipantsApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PermissionsApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PredefinedActivitiesApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PublicAuthApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/SessionApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingGroupsApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingStatsApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingTimesApi.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiErrorMessage.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiException.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/AuthedHttpClient.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/HttpClientEngineFactory.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/PublicHttpClient.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/AccidentDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Club.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ClubPermissionHelpers.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryActivityExtras.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDate.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDateActivityItem.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryImagePaths.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryMemberDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryPlanDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryTrainingParticipant.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginRequest.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginResponse.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Member.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberActivityDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberContactDto.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberGroupPhotoDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberImageDto.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetBody.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetMappers.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ParticipantStatusRequest.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PredefinedActivityDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PublicAuthDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/SessionStatusResponse.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingGroupDtos.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingStats.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/UserClubPermissions.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/MobileStrings.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/README.md create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthManager.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthState.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthTokens.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubManager.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubState.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubStorage.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryManager.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryState.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ErrorMapper.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageManager.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageState.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageStorage.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersManager.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersState.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MutableTokenProvider.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenProvider.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenStorage.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsManager.kt create mode 100644 mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsState.kt create mode 100644 mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/api/http/IosHttpClientEngineFactory.kt create mode 100644 mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosClubStorage.kt create mode 100644 mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosTokenStorage.kt create mode 100644 mobile-app_legacy_rn_expo/.gitignore create mode 100644 mobile-app_legacy_rn_expo/App.tsx create mode 100644 mobile-app_legacy_rn_expo/README.md create mode 100644 mobile-app_legacy_rn_expo/app.json create mode 100644 mobile-app_legacy_rn_expo/assets/adaptive-icon.png create mode 100644 mobile-app_legacy_rn_expo/assets/favicon.png create mode 100644 mobile-app_legacy_rn_expo/assets/icon.png create mode 100644 mobile-app_legacy_rn_expo/assets/splash-icon.png create mode 100644 mobile-app_legacy_rn_expo/index.ts create mode 100644 mobile-app_legacy_rn_expo/package-lock.json create mode 100644 mobile-app_legacy_rn_expo/package.json create mode 100644 mobile-app_legacy_rn_expo/src/api/apiClient.ts create mode 100644 mobile-app_legacy_rn_expo/src/config/env.ts create mode 100644 mobile-app_legacy_rn_expo/src/i18n/index.ts create mode 100644 mobile-app_legacy_rn_expo/src/navigation/AppNavigation.tsx create mode 100644 mobile-app_legacy_rn_expo/src/screens/HomeScreen.tsx create mode 100644 mobile-app_legacy_rn_expo/src/screens/LoginScreen.tsx create mode 100644 mobile-app_legacy_rn_expo/src/state/auth/AuthContext.tsx create mode 100644 mobile-app_legacy_rn_expo/src/state/auth/authStore.ts create mode 100644 mobile-app_legacy_rn_expo/src/state/auth/storage.ts create mode 100644 mobile-app_legacy_rn_expo/tsconfig.json create mode 100644 scripts/generate-mobile-i18n.js diff --git a/.gitignore b/.gitignore index 2607a293..7cfc38a0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,11 @@ backend/images/* backend/backend-debug.log backend/*.log backend/.env.local + +mobile-app/.gradle/ +mobile-app/.idea/ +mobile-app/.kotlin/ +mobile-app/build/ +mobile-app/composeApp/build/ +mobile-app/shared/build/ +mobile-app/local.properties diff --git a/backend/controllers/diaryMemberController.js b/backend/controllers/diaryMemberController.js index 924db041..29b2d8a7 100644 --- a/backend/controllers/diaryMemberController.js +++ b/backend/controllers/diaryMemberController.js @@ -61,8 +61,12 @@ const removeMemberNote = async (req, res) => { try { const { authcode: userToken } = req.headers; const { clubId, noteId } = req.params; + const { diaryDateId, memberId } = req.query; + if (!diaryDateId || !memberId) { + return res.status(400).json({ error: 'diaryDateId and memberId query parameters are required' }); + } await DiaryMemberService.removeNoteFromMember(userToken, clubId, noteId); - const notes = await DiaryMemberService.getNotesForMember(userToken, req.params.clubId, diaryDateId, memberId); + const notes = await DiaryMemberService.getNotesForMember(userToken, clubId, diaryDateId, memberId); res.status(200).json(notes); } catch (error) { console.error('[removeMemberNote] - Error: ', error.message); @@ -74,7 +78,7 @@ const removeMemberTag = async (req, res) => { try { const { diaryDateId, memberId, tagId } = req.body; const { authcode: userToken } = req.headers; - await DiaryMemberService.removeTagFromMemberAndDate(userToken, req.params.clubId, diaryDateId, memberId, tagId); + await DiaryMemberService.removeTagFromMemberAndDate(userToken, req.params.clubId, diaryDateId, memberId, { id: tagId }); const tags = await DiaryMemberService.getTagsForMemberAndDate(userToken, req.params.clubId, diaryDateId, memberId); res.status(200).json(tags); } catch (error) { diff --git a/backend/services/diaryMemberService.js b/backend/services/diaryMemberService.js index fdf56490..d9074bbd 100644 --- a/backend/services/diaryMemberService.js +++ b/backend/services/diaryMemberService.js @@ -51,7 +51,7 @@ class DiaryMemberService { if (tagLink) { await tagLink.destroy(); } else { - devLog(diaryDateId, memberId, tagId); + devLog(diaryDateId, memberId, tag?.id); throw new Error('Das Tag ist nicht verknüpft.'); } } diff --git a/docs/MOBILE_APP_KMP_PLAN.md b/docs/MOBILE_APP_KMP_PLAN.md new file mode 100644 index 00000000..9ee1907b --- /dev/null +++ b/docs/MOBILE_APP_KMP_PLAN.md @@ -0,0 +1,106 @@ +# Mobile App Restart (KMP) – Plan + +Ziel: Eine **wirklich native** Mobile App (Android + iOS) mit **gemeinsamem Shared-Code** (Kotlin Multiplatform) und nativer UI. + +Wichtiger Befund aus dem Repo: Die Auth ist **token-basiert (JWT)**, nicht Cookie-Session: +- `POST /api/auth/login` → `{ token }` +- Token wird bei Requests als Header `authcode: ` (Web-Standard) gesendet. +- Alternativ akzeptiert das Backend auch `Authorization: Bearer ` (Middleware). + +## 1) Ziel-Architektur + +### Module +- `mobile-app/shared/` + - Domain/UseCases + - API Client (Ktor) + - Token Storage Abstraktion + - Repositories + - (später) Offline Cache +- `mobile-app/androidApp/` (oder bestehend: `composeApp` Android target) + - Android UI (Jetpack Compose) + - Android-spezifische Implementierungen (Secure storage, logging) +- `mobile-app/iosApp/` + - iOS UI (SwiftUI) **oder** Compose Multiplatform UI + - iOS-spezifische Implementierungen (Keychain, logging) + +### UI-Strategie (2 sinnvolle Wege) +**A) Native UI je Plattform (Compose + SwiftUI)** (empfohlen für “wirklich native”) +- Shared: Logik/Netzwerk/State +- Android UI: Compose +- iOS UI: SwiftUI + +**B) Gemeinsame UI mit Compose Multiplatform** +- Shared: UI + Logik +- Android/iOS nutzen die gleiche Compose-UI +- Weniger UI-Duplizierung, aber iOS “native feel” erfordert extra Pflege. + +Empfehlung: Start mit **A**, da iOS dann maximal “native” ist; Shared bleibt groß genug, dass der doppelte UI-Aufwand beherrschbar bleibt. + +## 2) Networking & Auth (Shared) + +### Endpunkte (v1) +- Login: `POST /api/auth/login` +- Logout: `POST /api/auth/logout` (Token wird serverseitig invalidiert) +- Session Check: `GET /api/session/status` (auth erforderlich) + +### Request-Regeln +- Header `authcode` auf **jedem** API Request setzen (wie Web). +- Globales Handling: + - 401 → Token löschen, UI zurück zu Login. + - Timeouts/Netzfehler → kein Auto-Logout. + +### Implementierungsvorschlag +- Ktor Client: + - `ContentNegotiation` + Kotlinx Serialization + - `HttpTimeout` + - DefaultRequest: `url(baseUrl)`, `header("authcode", token)` +- Token Storage: + - Interface im `shared` + - Android: EncryptedSharedPreferences oder Jetpack Security + - iOS: Keychain + +Aktueller Stand im Repo: +- `TokenStorage` + Android/iOS Implementierungen liegen in `mobile-app/shared/src/**/de/tt_tagebuch/shared/state/*TokenStorage*`. + +## 3) Parity-Plan (“wie Webapp”, aber in Wellen) +“Komplett wie Webapp” wird als **Feature-Parität** umgesetzt, nicht als Big-Bang. + +### Release 1 (Must-have) +- Auth: Login/Logout/Passwort reset (wenn benötigt) +- Club-Kontext: Verein wählen, Berechtigungen/Access Requests +- Tagebuch: Liste/Detail/CRUD + Filter/Suche +- Mitglieder: Liste/Detail + Filter/Suche + +### Parity-Wellen +Welle A: Training Stats, Personal Settings +Welle B: Approvals, Club Settings, Member Transfer +Welle C: Team Management, Permissions, Logs +Welle D: Orders + Billing +Welle E: Tournaments + Schedule + CourtDrawingTool + PDF/Exports + Upload/Crop + +Jede Welle: API-Gaps schließen → UI → Tests → Release. + +## 4) i18n-Strategie (Single Source of Truth) +Quelle bleibt `frontend/src/i18n/locales/de.json`. + +Option 1 (empfohlen): **Generator** +- Script generiert: + - `shared`-Resources (Keys/Strings) oder + - Android `strings.xml` + iOS `.strings` +- Vorteil: Keys bleiben identisch, kein Drift. + +Option 2: JSON direkt in App laden +- Einfacher Start, aber weniger “platform idiomatic”. + +## 5) Repo/Build Setup +- Gradle Wrapper (`mobile-app/gradlew`) für reproduzierbare Builds. + - CI (später): + - Android: `./gradlew :androidApp:assembleDebug` + - iOS: Xcode build (oder Fastlane) + +## 6) Definition of Done (pro Feature) +- API Requests laufen stabil (Retry/Timeout ok) +- 401 führt zuverlässig zu Logout +- i18n Keys vorhanden (Generator/Checks) +- Android: Emulator + Device Smoke +- iOS: Simulator Smoke diff --git a/frontend/src/components/tournament/TournamentParticipantsTab.vue b/frontend/src/components/tournament/TournamentParticipantsTab.vue index 1a20ecbe..12c5f15e 100644 --- a/frontend/src/components/tournament/TournamentParticipantsTab.vue +++ b/frontend/src/components/tournament/TournamentParticipantsTab.vue @@ -1183,7 +1183,7 @@ export default { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; - touch-action: pan-x; + touch-action: pan-x pan-y; overscroll-behavior-x: contain; } @@ -1196,7 +1196,7 @@ export default { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; - touch-action: pan-x; + touch-action: pan-x pan-y; } .participants-class-section { @@ -1204,7 +1204,7 @@ export default { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; - touch-action: pan-x; + touch-action: pan-x pan-y; } .participants-table { @@ -1221,8 +1221,9 @@ export default { overflow-y: auto; overflow-x: auto; -webkit-overflow-scrolling: touch; - touch-action: pan-x; + touch-action: pan-x pan-y; overscroll-behavior-x: contain; + overscroll-behavior-y: contain; } .participants-table-unified thead th { diff --git a/frontend/src/views/TournamentTab.vue b/frontend/src/views/TournamentTab.vue index 79d666ee..0da6d141 100644 --- a/frontend/src/views/TournamentTab.vue +++ b/frontend/src/views/TournamentTab.vue @@ -3801,24 +3801,21 @@ export default { const participantsResponse = await apiClient.get(`/participants/${trainingForDate.id}`); const participants = participantsResponse.data; + const presentParticipants = Array.isArray(participants) + ? participants.filter(participant => participant.attendanceStatus === 'present') + : []; - if (participants && participants.length > 0) { - // Lade die Member-Details für jeden Teilnehmer + if (presentParticipants.length > 0) { + const membersResponse = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`); + const membersById = new Map((membersResponse.data || []).map(member => [Number(member.id), member])); const trainingParticipants = []; - for (const participant of participants) { - try { - // Lade Member-Details über die Member-API - const memberResponse = await apiClient.get(`/clubmembers/get/${this.currentClub}/true`); - const member = memberResponse.data.find(m => m.id === participant.memberId); - - if (member) { - trainingParticipants.push({ - clubMemberId: participant.memberId, - member: member - }); - } - } catch (memberError) { - console.error('Fehler beim Laden der Member-Details:', memberError); + for (const participant of presentParticipants) { + const member = membersById.get(Number(participant.memberId)); + if (member) { + trainingParticipants.push({ + clubMemberId: participant.memberId, + member: member + }); } } @@ -4895,7 +4892,7 @@ button { width: 100%; max-width: 100%; -webkit-overflow-scrolling: touch; - touch-action: pan-x; + touch-action: pan-x pan-y; } .participants-table { @@ -4926,7 +4923,8 @@ button { overflow-x: auto; margin: 0; -webkit-overflow-scrolling: touch; - touch-action: pan-x; + touch-action: pan-x pan-y; + overscroll-behavior-y: contain; } .participants-table-body td { diff --git a/mobile-app/DEVELOPMENT.md b/mobile-app/DEVELOPMENT.md new file mode 100644 index 00000000..ffc81e50 --- /dev/null +++ b/mobile-app/DEVELOPMENT.md @@ -0,0 +1,81 @@ +# Development + +## Backend-URL + +**Standard:** `https://tt-tagebuch.de` – gesetzt in `gradle.properties` (`backendBaseUrl=…`) und als Fallback in `composeApp/build.gradle.kts`. + +Für lokale Entwicklung läuft der Backend-Server auf deinem Rechner typischerweise unter **`http://localhost:3005`**. + +Die **Android-Studio-VM (Emulator)** kann **`localhost` / `127.0.0.1` nicht** für diesen lokalen Server nutzen: dort ist „localhost“ der Emulator selbst, nicht dein PC. Für lokale Emulator-Tests nutze `http://10.0.2.2:3005`. + +**HTTP / Cleartext:** Ab Android 9 blockiert das System unverschlüsseltes HTTP standardmäßig. Für die lokale Dev-URL ist das in `composeApp/src/androidMain/res/xml/network_security_config.xml` für `10.0.2.2`, `localhost` und `127.0.0.1` freigegeben. +Testest du mit der **LAN-IP deines PCs** (echtes Gerät), musst du diese IP dort als weiteres `` ergänzen oder kurzzeitig HTTPS nutzen. + +### Wenn die App trotz Rebuild noch `localhost` zeigt + +1. **Android Studio überschreibt Gradle:** + **Settings → Build, Execution, Deployment → Build Tools → Gradle** + beim Projekt `mobile-app` prüfen, ob unter **Command-line options** etwas wie + `-PbackendBaseUrl=http://localhost:3005` steht – das entfernen oder bewusst setzen. + +2. **Alte Installation / Build-Cache:** Emulator-App deinstallieren, Clean, neu installieren: + ```bash + cd mobile-app + adb uninstall de.tt_tagebuch.app + ./gradlew :composeApp:clean :composeApp:installDebug --no-configuration-cache + ``` + Oder das Skript: `./scripts/install-debug-emulator.sh` + +3. **BuildConfig prüfen:** Nach dem Build in + `composeApp/build/generated/.../BuildConfig.java` + den Wert von `BACKEND_BASE_URL` ansehen – der muss `https://tt-tagebuch.de` sein (oder deine bewusste Override-URL). + +### Android Studio „Run“ geht nicht + +- Projekt **`mobile-app/`** als Root öffnen (nicht nur den übergeordneten Monorepo-Ordner, wenn Studio das Modul nicht erkennt). +- **Run → Edit Configurations → Android App:** Modul **`composeApp`** wählen. +- Alternativ immer über die Kommandozeile installieren (siehe oben) und die App im Emulator starten. + +Überschreiben per Gradle-Property, z. B. lokaler Emulator: + +```bash +./gradlew :composeApp:installDebug -PbackendBaseUrl=http://10.0.2.2:3005 +``` + +**Alternative:** Port vom PC auf den Emulator weiterleiten, dann geht wieder `localhost`: + +```bash +adb reverse tcp:3005 tcp:3005 +./gradlew :composeApp:installDebug -PbackendBaseUrl=http://localhost:3005 +``` + +## Smoke flow (Android) +1. Start backend (default `http://localhost:3005`) +2. Start Android emulator +3. App installieren: `./scripts/install-debug-emulator.sh` oder Run in Studio (Modul **`composeApp`**) +4. Login → Club auswählen → Rolle wird angezeigt +5. Open `Tagebuch` → die letzten Einträge werden angezeigt + +## Gradle Wrapper +Use the wrapper from `mobile-app/`: +```bash +cd mobile-app +./gradlew tasks +``` + +Note (Codex sandbox only): the sandbox home directory is read-only, so run with: +```bash +GRADLE_USER_HOME=/tmp/gradle-home ./gradlew tasks +``` + +## i18n generation +```bash +node scripts/generate-mobile-i18n.js +``` + +This reads all web locale JSON files and writes the generated KMP bundle to +`mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/MobileStrings.kt`. + +## Build +Recommended: +- Open `mobile-app/` in Android Studio and run the `composeApp` configuration. diff --git a/mobile-app/README.md b/mobile-app/README.md new file mode 100644 index 00000000..1e972bae --- /dev/null +++ b/mobile-app/README.md @@ -0,0 +1,16 @@ +# Mobile App (Native) – Kotlin Multiplatform + +Dieses Verzeichnis ist der Neustart der Mobile App als **wirklich native Android+iOS App** via **Kotlin Multiplatform (KMP)**. + +## Status +- Der erste Versuch lag vorher in `mobile-app/` und wurde als Legacy gesichert. +- Ein React-Native/Expo Restart-Versuch liegt in `mobile-app_legacy_rn_expo/` (nicht mehr der aktuelle Weg). + +## Auth (wichtig) +Das Backend nutzt für die App/API **JWT Tokens**: +- `POST /api/auth/login` → `{ token }` +- Requests senden den Token als Header `authcode: ` (wie Webapp). + +## Plan +Siehe `docs/MOBILE_APP_KMP_PLAN.md`. + diff --git a/mobile-app/TODO.md b/mobile-app/TODO.md new file mode 100644 index 00000000..bef35b9a --- /dev/null +++ b/mobile-app/TODO.md @@ -0,0 +1,212 @@ +# Mobile App – TODO Web-Parität (Android, KMP-Shared) + +Dieses Dokument ist die **Arbeitsliste**, um die **funktionale Abdeckung der Web-App** (`frontend/src/router.js`, Views unter `frontend/src/views/`) in der **nativen Android-App** (Jetpack Compose, Shared Code unter `mobile-app/shared`) nachzubauen. + +**Wichtig:** Ein „komplettes“ Umsetzen dieser Liste ist ein **Mehrmonats-/Team-Projekt**. Es wird **iterativ** abgearbeitet; unten sind nur die Punkte angehakt, die im Code **tatsächlich** vorhanden sind (Stand siehe Git). Alles andere bleibt offen. + +**Legende:** `[x]` umgesetzt · `[ ]` offen · Die „Baseline“ am Ende beschreibt den älteren Grundstock. + +**Vorgehen:** Pro Phase vertikale Schnitte (API im `shared` Modul → Manager/Use-Case → UI). Web-Referenz immer die gleichnamige `*.vue`-Datei und `apiClient`-Aufrufe darin. + +--- + +## Bereits umgesetzt (Baseline, Stand 2026-05) + +- Auth (`authcode`), Token-Persistenz, 401 → Login +- Club-Auswahl, Permissions, Access-Request +- Tabs inkl. **Start**-Hub (`MainTab.Home`), Unter-Screens (Tagebuch-Tag, Mitglied-Detail), Tab-/Rail ausblenden in Details +- **Tagebuch:** Datenliste, Tag-Detail, Zeiten, Notizen, Tags (Tagesbezug), **Freitext-Aktivitäten** (`/activities`), Trainingsplan **lesen & CRUD** (Phase 3.2), Teilnehmer an/ab (einklappbare Liste, standard eingeklappt), **Zuordnung zu Plan-Aktivitäten** (3.4), **Unfälle/Vorfälle** (3.7), Löschen Tag +- **Mitglieder:** Liste, Suche, statisches Detail +- **Trainings-Statistik:** Basis-KPIs + Top-Liste +- **Einstellungen:** Sprache, Session-Check, Logout, Backend-Anzeige +- i18n-Generator + `MobileStrings` +- Theming näher an Web (`TtTagebuchTheme`) + +--- + +## Phase 0 – Architektur & Grundlagen für Vollausbau + +- [ ] **Navigation:** Zentraler Nav-Graph (z. B. Navigation Compose) mit Back-Stack pro Tab oder einheitlichem Stack; Tiefe: Club → Modul → Unterseiten +- [ ] **Feature-Paketierung:** UI/API pro Bereich trennen (`diary`, `members`, `schedule`, …), Composables entschlacken +- [ ] **Use-Cases:** Geschäftslogik aus Composables in testbare Funktionen/Klassen im `shared` +- [x] **Fehlerbild (Basis):** JSON-Fehlerbody (`error` / `message`) aus API-Antworten → [ApiException]-Text (`ApiErrorMessage.kt`, authed + public Client); bekannte Tokens (`alreadyexists`, …) → Deutsch; Retries bewusst noch offen +- [ ] **Echtzeit (optional):** Socket-Events wie Web (`socketService`) oder dokumentiertes Polling nach Schreiboperationen +- [ ] **Medien:** Entscheidung Bilder/PDF (Coil, Cache, Download, Intents) +- [x] **Öffentlicher HTTP-Client** ohne Auth-Header (`PublicHttpClient`, `PublicAuthApi` im `shared`) + +--- + +## Phase 1 – Auth, Onboarding, öffentliche Seiten + +Web-Routen: `/login`, `/register`, `/activate/:code`, `/forgot-password`, `/reset-password/:token`, `/impressum`, `/datenschutz`, `/` (Home öffentlich) + +- [x] **Registrierung** (`Register.vue`) – Android: `RegisterScreen`, `POST /api/auth/register` +- [x] **Account aktivieren** (`Activate.vue`) – Android: `ActivateAccountScreen`, `GET /api/auth/activate/:code` +- [x] **Passwort vergessen / E-Mail** (`ForgotPassword.vue`) – `ForgotPasswordScreen`, `POST /api/auth/forgot-password` +- [x] **Passwort setzen mit Token** (`ResetPassword.vue`) – `ResetPasswordScreen`, `POST /api/auth/reset-password` +- [x] **Home / Landing eingeloggt** (`Home.vue`) – Tab **Start**: Willkommen, Kacheln zu Tagebuch/Mitglieder/Statistik/Mehr, Vereinsinfos via `GET /api/clubs/:id` (`HomeScreen`, `ClubsApi.getClub`) +- [x] **Impressum** – Einstellungen → öffnet `BACKEND_BASE_URL/impressum` im Browser +- [x] **Datenschutz** – Einstellungen → öffnet `BACKEND_BASE_URL/datenschutz` im Browser +- [ ] SEO-Marketingseiten (`TableTennisClubSoftware`, `ClubMemberManagementPage`, …): **nur falls** im Store gefordert; sonst `[ ] optional / nicht mobil` + +--- + +## Phase 2 – Verein anlegen & Vereins-Ansicht + +- [x] **Verein erstellen** (`CreateClub.vue`) + API – `POST /api/clubs`, `ClubSelectScreen` + `ClubManager.createClub` +- [x] **Vereins-Profil / Show Club** (`ClubView.vue`) – Basis: erweitertes `Club`-Modell + Karte auf **Start** (Begrüßung, Verbands-Nr., MyTischtennis-Kürzel); volle Parität (Links, Zahlen) offen + +--- + +## Phase 3 – Tagebuch (DiaryView) – Hauptblock + +Web: `DiaryView.vue` (sehr groß). API-Cluster (Auszug – in Web nach `apiClient` verifizieren): + +### 3.1 Tages-Metadaten & Listen + +- [x] Tagebuch-Daten **vollständig** laden beim Datumswechsel – Plan, Trainingsgruppen und Teilnehmer **parallel** in einem `LaunchedEffect` (`DiaryDetailScreen`) +- [x] **Aktivitäten-Liste** `/activities/:dateId` — `DiaryApi.listFreeformActivities` / `addFreeformActivity`, Abschnitt „Weitere Tages-Aktivitäten“ im Tagebuch-Detail + +### 3.2 Trainingsplan (CRUD) + +- [x] Plan laden: `GET /diary-date-activities/:clubId/:diaryDateId` — `DiaryApi` / Tagebuch-Detail +- [x] Einträge anlegen: `POST /diary-date-activities/:clubId`, ggf. **Gruppe** `POST /diary-date-activities/group` — Android-Formulare + `CreateDiaryPlanActivityRequest` / `AddDiaryPlanGroupActivityRequest` +- [x] Einträge bearbeiten: `PUT /diary-date-activities/:clubId/:id` (Zuordnung `groupId` im Body wie Backend) — Dialog „Bearbeiten“; Unter-Einträge (`GroupActivity`): API `updateNestedGroupActivity`, mobil z. B. nur **Löschen** +- [x] Reihenfolge: `PUT .../order` — ↑/↓ je Trainingsgruppen-Scope +- [x] Löschen: `DELETE ...`, Gruppen-Aktivität `DELETE .../group/...` +- [x] **Zeitblöcke** (`isTimeblock`) inkl. UX wie Web — Checkbox beim Anlegen, Kennzeichnung in der Karte +- [x] **Gruppen** für Plan: `GET/POST/DELETE /api/group` — `GroupApi` + Liste mit Löschen; **PUT** Umbenennen (`changeGroup`) nur API, keine eigene UI + +### 3.3 Teilnehmer & Status + +- [x] Liste: `GET /participants/:dateId` +- [x] Hinzufügen/Entfernen: `POST /participants/add`, `POST /participants/remove` +- [x] Status **entschuldigt/abgesagt**: `PUT /participants/:dateId/:memberId/status` +- [x] **Gruppenzuordnung Training**: `PUT /participants/:dateId/:memberId/group` — `ParticipantsApi.updateParticipantGroup`, Dropdown bei anwesenden Teilnehmern wenn Trainingsgruppen existieren +- [x] **UX:** Teilnehmerliste im Tagebuch-Detail **aufklappbar**, standard **eingeklappt** (`DiaryDetailScreen`) + +- [x] `GET /diary-member-activities/:clubId/:activityId` — `DiaryMemberActivitiesApi` / „Wer macht mit?“ je Planzeile bzw. Gruppen-Aktivität +- [x] Zuweisen: `POST ...` mit `participantIds` — inkl. `ensureParticipantRowId` wenn noch keine Teilnehmer-Zeile +- [x] Entfernen: `DELETE .../:participantId` + +### 3.5 Mitgliedsbezogene Notizen/Tags im Tagebuch + +- [x] `GET/POST .../diarymember/:clubId/note`, `DELETE .../note/:id` (mit `diaryDateId`/`memberId` als Query) — `DiaryMemberApi` / Dialog „Notizen & Tags“ je Teilnehmer +- [x] `GET/POST .../diarymember/:clubId/tag`, `POST .../tag/remove` — bestehende Tags, neu anlegen (`POST /tags` + Verknüpfung) + +### 3.6 Predefined Activities (Auswahl & Suche im Tagebuch) + +- [x] `GET /predefined-activities`, `GET ...?scope=standard`, `GET /search/query` — Trainingsplan anlegen (global + Gruppe) und Eintrag bearbeiten (Suche / vordefinierte ID) +- [x] `GET /predefined-activities/:id` — `PredefinedActivitiesApi.getById` / `DiaryManager.getPredefinedActivity` (für spätere Detail-UI) + +### 3.7 Unfälle / Vorfälle + +- [x] `POST /accident`, `GET /accident/:clubId/:diaryDateId` — `AccidentApi` / `DiaryManager` / Abschnitt im Tagebuch-Detail + +### 3.8 Galerie & Bilder + +- [x] Mitgliederbilder: `memberProfileImagePath` + `AuthenticatedAsyncImage` (Coil mit `diaryAuthHeaders`) in der Teilnehmerliste bei `diary.read` + `members.read` +- [x] Übungs-/Predefined-Bilder: `DiaryImagePaths` (`mainActivityImagePath` / `nestedActivityImagePath`) + `ApiConfig.toAbsoluteUrl`, Vollbild-Dialog im Tagebuch-Detail +- [x] **Gruppenfotos:** `MemberGroupPhotosApi` (`GET/POST/DELETE /api/member-group-photos/:clubId`), Galerie-Abschnitt + `PickVisualMedia` in `DiaryDetailScreen` + +### 3.9 PDF / Export + +- [x] Trainingsplan- und Tages-PDF (`DiaryPdfExporter` / `writeTrainingPlanPdf`, `writeTrainingDaySummaryPdf`), Teilen über `FileProvider` + `sharePdfFile` (`DiaryPdfShare`) + +### 3.10 Sonstiges Diary-UX + +- [x] **Web-DiaryView → mobil (Kurzüberblick):** Tages-Metadaten, Plan inkl. Zeitblöcke/Gruppen, Teilnehmer inkl. Status/Gruppe, „Wer macht mit?“, Freitext-Aktivitäten, Tags/Notizen, Unfälle, Mitglieds-Notizen/Tags-Dialog, Galerie, PDF-Exporte – ohne die vielen Web-Tabs als 1:1-Spiegel; fehlende Parität steht in den Phasen 4+ / offenen Punkten. +- [x] **Berechtigungen:** `ClubPermissionHelpers` (`canReadDiary`, `canWriteDiary`, `canReadMembers`, `canWriteMembers`) – Lese-Hinweis, Schreib-Aktionen und Gruppenfoto-Löschen in `AppRoot.kt` / `DiaryDetailScreen` entsprechend gekapselt + +--- + +## Phase 4 – Mitglieder (MembersView) — erledigt + +- [x] **Mitglied-CRUD:** `POST /api/clubmembers/set/:clubId` — `MembersApi.setMember`, `MemberSetBody` / `Member.toSetBody`, `MembersManager.saveMember`; neu + bearbeiten in `MemberEditRoute` (`AppRoot.kt`) +- [x] **Felder / Formulare:** Stammdaten, Adresse, **mehrere Telefon- und E-Mail-Zeilen** (Primär, Elternkontakt, Name) wie Web-`contacts`, Status (aktiv, Testmitglied, Formular, Freigaben, Bilder Internet), Geschlecht; TTR/QTTR nur Anzeige beim Bearbeiten +- [x] **Profilbild:** `POST /api/clubmembers/image/...` (Multipart) — `MembersApi.uploadMemberPortrait`, Galerie-Pick + UCrop in `MemberDetailRoute` / `MemberPortraitCrop.kt` +- [x] **Aktivität / letzte Teilnahmen:** `GET /api/member-activities/:clubId/:memberId` (Perioden-Query), `.../last-participations` — `MemberActivitiesApi`, Abschnitte im Profil +- [x] **Trainingsgruppen:** `GET/POST/DELETE /api/training-groups/...` — `TrainingGroupsApi`, Zuweisen/Entfernen im Profil +- [x] **Trainingszeiten (Verein):** `GET /api/training-times/:clubId` — `TrainingTimesApi`, Anzeige je Gruppe im Profil +- [x] **Bild zuschneiden:** Yalantis **UCrop** nach `PickVisualMedia` — JitPack, `UCropActivity` im Manifest + +--- + +## Phase 5 – Trainings-Statistik (Parität TrainingStatsView) + +- [ ] Alle Kennzahlen/Tabellen/Filter aus Web +- [ ] Zeiträume, Exporte, falls vorhanden + +--- + +## Phase 6 – Terminplan (ScheduleView) + +- [ ] Kalender-/Listenansicht, CRUD oder Sync wie Web +- [ ] API-Endpunkte aus `ScheduleView.vue` ins `shared` übernehmen + +--- + +## Phase 7 – Turniere + +- [ ] `TournamentsView.vue` – Vereinsturniere +- [ ] `OfficialTournaments.vue` / offizielle Teilnahmen +- [ ] `TournamentTab.vue` – eingebettete Logik, soweit mobil relevant +- [ ] API aus jeweiligen Views dokumentieren und abarbeiten + +--- + +## Phase 8 – Freigaben & Verwaltung + +- [ ] **Ausstehende Freigaben** (`PendingApprovalsView.vue`) +- [ ] **Team-Management** (`TeamManagementView.vue`) +- [ ] **Berechtigungen** (`PermissionsView.vue`) – rollenbasiert +- [ ] **Logs** (`LogsView.vue`) – eher Admin; nur wenn nötig mobil + +--- + +## Phase 9 – Vereins- & Stammdaten-Einstellungen + +- [ ] **ClubSettings** (`ClubSettings.vue`) – alle Unterbereiche +- [ ] **Predefined Activities Verwaltung** (`PredefinedActivities.vue`) – CRUD, Bilder/Zeichnungen falls API +- [ ] **Mitgliedstransfer-Einstellungen** (`MemberTransferSettingsView.vue`) + +--- + +## Phase 10 – Persönliche Konten & Integrationen + +- [ ] **Persönliche Einstellungen** vollständig (`PersonalSettings.vue` vs. aktuelles „Mehr“) +- [ ] **MyTischtennis-Konto** (`MyTischtennisAccount.vue`) +- [ ] **ClickTT-Konto** (`ClickTtAccount.vue`) +- [ ] **ClickTT-Ansicht** (`ClickTtView.vue`) + +--- + +## Phase 11 – Abrechnung & Bestellungen + +- [ ] **Orders** (`OrdersView.vue`) +- [ ] **Billing** (`BillingView.vue`) +- [ ] Rechtliches/UX: ggf. WebView oder Deep-Link, wenn Zahlungsflüsse web-only + +--- + +## Phase 12 – Qualität, Tests, Release + +- [ ] **Regression-Checkliste** pro Phase (manuell) +- [ ] Automatisierte Tests: `shared` (Serialisierung, Mapper), wo möglich UI-Tests kritische Flows +- [ ] **Barrierefreiheit:** Talkback, Kontraste, Touch-Ziele +- [ ] **Performance:** große Listen (Paging), Bildcache +- [ ] Store-Texte, Datenverarbeitung (Play Policy) für Medien und Kontakte + +--- + +## Hinweise zur Pflege dieser Liste + +1. Beim Abhaken eines Punktes **kurz** vermerken (Commit-Message oder Unterpunkt), welche Datei/API neu ist. +2. Wenn Web eine Funktion **einstellt** oder API ändert: TODO hier und `DEVELOPMENT.md` anpassen. +3. **DiaryView** zuerst in **Unterkapitel** zerlegen (3.1–3.10), dann Issues/PRs pro Unterkapitel – sonst bleibt der Block unendlich. + +--- + +## Referenz: Web-Routen (Router) + +Siehe `frontend/src/router.js` – jede `path`-Zeile sollte langfristig einem mobilen Eintrag (oder einer bewussten Ausnahme) zugeordnet sein. diff --git a/mobile-app/composeApp/build.gradle.kts b/mobile-app/composeApp/build.gradle.kts index c63f5e55..6893ad6b 100644 --- a/mobile-app/composeApp/build.gradle.kts +++ b/mobile-app/composeApp/build.gradle.kts @@ -1,3 +1,9 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +val backendBaseUrl = providers.gradleProperty("backendBaseUrl") + .orElse("https://tt-tagebuch.de") + .get() + plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.androidApplication) @@ -8,8 +14,8 @@ plugins { kotlin { androidTarget { compilations.all { - kotlinOptions { - jvmTarget = "1.8" + compilerOptions.configure { + jvmTarget.set(JvmTarget.JVM_17) } } } @@ -20,6 +26,7 @@ kotlin { implementation(compose.runtime) implementation(compose.foundation) implementation(compose.material) + implementation(compose.materialIconsExtended) implementation(compose.ui) implementation(compose.components.resources) implementation(libs.voyager.navigator) @@ -32,6 +39,8 @@ kotlin { implementation(libs.androidx.appcompat) implementation(libs.androidx.core.ktx) implementation(libs.koin.android) + implementation(libs.coil.compose) + implementation(libs.yalantis.ucrop) } } } @@ -46,6 +55,10 @@ android { targetSdk = libs.versions.android.targetSdk.get().toInt() versionCode = 1 versionName = "1.0.0" + buildConfigField("String", "BACKEND_BASE_URL", "\"$backendBaseUrl\"") + } + buildFeatures { + buildConfig = true } packaging { resources { @@ -58,8 +71,8 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } } diff --git a/mobile-app/composeApp/src/androidMain/AndroidManifest.xml b/mobile-app/composeApp/src/androidMain/AndroidManifest.xml index e704d293..9ae3a259 100644 --- a/mobile-app/composeApp/src/androidMain/AndroidManifest.xml +++ b/mobile-app/composeApp/src/androidMain/AndroidManifest.xml @@ -8,18 +8,34 @@ android:allowBackup="true" android:icon="@android:mipmap/sym_def_app_icon" android:label="Trainingstagebuch" + android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@android:mipmap/sym_def_app_icon" android:supportsRtl="true" android:theme="@android:style/Theme.Material.Light.NoActionBar"> + android:theme="@android:style/Theme.Material.Light.NoActionBar" + android:windowSoftInputMode="adjustResize"> + + + + diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/AppDependencies.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/AppDependencies.kt new file mode 100644 index 00000000..5d01a5d2 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/AppDependencies.kt @@ -0,0 +1,116 @@ +package de.tt_tagebuch.app + +import android.content.Context +import android.content.Intent +import android.net.Uri +import de.tt_tagebuch.shared.api.AccidentApi +import de.tt_tagebuch.shared.api.ApiConfig +import de.tt_tagebuch.shared.api.AuthApi +import de.tt_tagebuch.shared.api.PublicAuthApi +import de.tt_tagebuch.shared.api.ClubsApi +import de.tt_tagebuch.shared.api.DiaryApi +import de.tt_tagebuch.shared.api.DiaryMemberActivitiesApi +import de.tt_tagebuch.shared.api.DiaryMemberApi +import de.tt_tagebuch.shared.api.GroupApi +import de.tt_tagebuch.shared.api.ParticipantsApi +import de.tt_tagebuch.shared.api.PredefinedActivitiesApi +import de.tt_tagebuch.shared.api.MemberActivitiesApi +import de.tt_tagebuch.shared.api.MemberGroupPhotosApi +import de.tt_tagebuch.shared.api.MembersApi +import de.tt_tagebuch.shared.api.PermissionsApi +import de.tt_tagebuch.shared.api.SessionApi +import de.tt_tagebuch.shared.api.TrainingGroupsApi +import de.tt_tagebuch.shared.api.TrainingStatsApi +import de.tt_tagebuch.shared.api.TrainingTimesApi +import de.tt_tagebuch.shared.api.http.AndroidHttpClientEngineFactory +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.http.PublicHttpClient +import de.tt_tagebuch.shared.state.AndroidClubStorage +import de.tt_tagebuch.shared.state.AndroidLanguageStorage +import de.tt_tagebuch.shared.state.AndroidTokenStorage +import de.tt_tagebuch.shared.state.AuthManager +import de.tt_tagebuch.shared.state.ClubManager +import de.tt_tagebuch.shared.state.DiaryManager +import de.tt_tagebuch.shared.state.LanguageManager +import de.tt_tagebuch.shared.state.MembersManager +import de.tt_tagebuch.shared.state.MutableTokenProvider +import de.tt_tagebuch.shared.state.TrainingStatsManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableStateFlow + +class AppDependencies(context: Context) { + private val appContext = context.applicationContext + private val applicationJob = SupervisorJob() + + /** + * Für Suspend-Aufrufe aus Button-Callbacks: überlebt das Verlassen der Composition + * (z. B. Club wählen → anderer Screen), im Gegensatz zu [rememberCoroutineScope]. + */ + val applicationScope = CoroutineScope(applicationJob + Dispatchers.Main.immediate) + val apiConfig = ApiConfig(baseUrl = BuildConfig.BACKEND_BASE_URL) + val unauthorizedEvents = MutableStateFlow(0) + private val tokenProvider = MutableTokenProvider() + private val client = AuthedHttpClient( + apiConfig = apiConfig, + tokenProvider = tokenProvider, + httpClientEngineFactory = AndroidHttpClientEngineFactory(), + onUnauthorized = { unauthorizedEvents.value += 1 }, + ) + + private val publicHttpClient = PublicHttpClient( + apiConfig = apiConfig, + httpClientEngineFactory = AndroidHttpClientEngineFactory(), + ) + + val publicAuthApi = PublicAuthApi(publicHttpClient) + + val authManager = AuthManager( + tokenProvider = tokenProvider, + tokenStorage = AndroidTokenStorage(context.applicationContext), + authApi = AuthApi(client), + sessionApi = SessionApi(client), + ) + + val clubManager = ClubManager( + clubStorage = AndroidClubStorage(context.applicationContext), + clubsApi = ClubsApi(client), + permissionsApi = PermissionsApi(client), + ) + + val diaryManager = DiaryManager( + DiaryApi(client), + ParticipantsApi(client), + GroupApi(client), + DiaryMemberActivitiesApi(client), + DiaryMemberApi(client), + PredefinedActivitiesApi(client), + AccidentApi(client), + MemberGroupPhotosApi(client), + ) + val membersManager = MembersManager( + MembersApi(client), + TrainingGroupsApi(client), + MemberActivitiesApi(client), + TrainingTimesApi(client), + ) + val trainingStatsManager = TrainingStatsManager(TrainingStatsApi(client)) + val languageManager = LanguageManager(AndroidLanguageStorage(context.applicationContext)) + val sessionApi = SessionApi(client) + + /** Header wie [AuthedHttpClient] (für Coil-Bilder, Downloads). */ + fun diaryAuthHeaders(): Map = buildMap { + tokenProvider.token?.let { put("authcode", it) } + tokenProvider.username?.let { put("userid", it) } + } + + /** Öffnet einen Pfad auf dem konfigurierten Backend im Browser (z. B. Impressum, Datenschutz). */ + fun openBackendPath(path: String) { + val base = apiConfig.baseUrl.trimEnd('/') + val suffix = path.trim().let { p -> if (p.startsWith("/")) p else "/$p" } + val uri = Uri.parse("$base$suffix") + val intent = Intent(Intent.ACTION_VIEW, uri).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + runCatching { appContext.startActivity(intent) } + } +} diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainActivity.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainActivity.kt index 6deeb488..d441d54e 100644 --- a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainActivity.kt +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainActivity.kt @@ -3,12 +3,36 @@ package de.tt_tagebuch.app import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import de.tt_tagebuch.app.ui.AppRoot +import de.tt_tagebuch.app.ui.TtTagebuchTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - App() + val context = LocalContext.current + val dependencies = remember { + try { + AppDependencies(context.applicationContext) + } catch (t: Throwable) { + android.util.Log.e("MainActivity", "Failed to initialize AppDependencies", t) + throw t + } + } + TtTagebuchTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background, + ) { + AppRoot(dependencies) + } + } } } } diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainApplication.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainApplication.kt index 80715784..f6eacc37 100644 --- a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainApplication.kt +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/MainApplication.kt @@ -1,18 +1,6 @@ package de.tt_tagebuch.app import android.app.Application -import de.tt_tagebuch.app.di.appModule -import de.tt_tagebuch.shared.di.initKoin -import org.koin.android.ext.koin.androidContext -class MainApplication : Application() { - override fun onCreate() { - super.onCreate() - initKoin( - baseUrl = "https://tt-tagebuch.de", - additionalModules = listOf(appModule) - ) { - androidContext(this@MainApplication) - } - } -} +class MainApplication : Application() + diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfExporter.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfExporter.kt new file mode 100644 index 00000000..e60a7631 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfExporter.kt @@ -0,0 +1,191 @@ +package de.tt_tagebuch.app.pdf + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Typeface +import android.graphics.pdf.PdfDocument +import android.text.Layout +import android.text.StaticLayout +import android.text.TextPaint +import de.tt_tagebuch.shared.api.models.DiaryDateActivityItem +import de.tt_tagebuch.shared.api.models.DiaryFreeformActivity +import de.tt_tagebuch.shared.api.models.DiaryTrainingParticipant +import de.tt_tagebuch.shared.api.models.Member +import de.tt_tagebuch.shared.api.models.displayTitle +import de.tt_tagebuch.shared.api.models.isPresentParticipant +import java.io.File +import java.io.FileOutputStream +import java.util.Locale + +private const val PAGE_W = 595 +private const val PAGE_H = 842 +private const val MARGIN = 40f +private const val MAX_TEXT_W = (PAGE_W - MARGIN * 2).toInt() + +private fun newTitlePaint(): TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.BLACK + textSize = 16f + typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) +} + +private fun newBodyPaint(): TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.BLACK + textSize = 11f + typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) + textLocale = Locale.GERMANY +} + +private fun Canvas.drawStatic(text: String, paint: TextPaint, x: Float, y: Float): Float { + if (text.isEmpty()) return y + val layout = StaticLayout.Builder.obtain(text, 0, text.length, paint, MAX_TEXT_W) + .setAlignment(Layout.Alignment.ALIGN_NORMAL) + .setLineSpacing(0f, 1.05f) + .setIncludePad(false) + .build() + save() + translate(x, y) + layout.draw(this) + restore() + return y + layout.height + 6f +} + +fun writeTrainingPlanPdf( + outFile: File, + clubName: String, + dateLabel: String, + timeLabel: String, + planItems: List, + timeblockFallback: String, +) { + val doc = PdfDocument() + var pageSeq = 0 + fun openPage(): PdfDocument.Page { + pageSeq++ + return doc.startPage(PdfDocument.PageInfo.Builder(PAGE_W, PAGE_H, pageSeq).create()) + } + var page = openPage() + var canvas = page.canvas + var y = MARGIN + val titlePaint = newTitlePaint() + val bodyPaint = newBodyPaint() + fun newPageIfNeeded(extra: Float) { + if (y + extra > PAGE_H - MARGIN) { + doc.finishPage(page) + page = openPage() + canvas = page.canvas + y = MARGIN + } + } + y = canvas.drawStatic("Trainingsplan – $clubName", titlePaint, MARGIN, y) + y = canvas.drawStatic("Datum: $dateLabel", bodyPaint, MARGIN, y) + y = canvas.drawStatic("Zeiten: $timeLabel", bodyPaint, MARGIN, y) + y = canvas.drawStatic("", bodyPaint, MARGIN, y) + val sorted = planItems.sortedWith(compareBy({ it.groupId ?: 0 }, { it.orderId }, { it.id })) + sorted.forEach { item -> + val line = buildString { + append(item.displayTitle(timeblockFallback).ifBlank { "Eintrag ${item.id}" }) + item.durationText?.takeIf { it.isNotBlank() }?.let { append(" — $it") } + ?: item.duration?.let { append(" — ${it} min") } + item.planGroup?.name?.takeIf { it.isNotBlank() }?.let { append(" — Gruppe: $it") } + } + newPageIfNeeded(40f) + y = canvas.drawStatic("• $line", bodyPaint, MARGIN, y) + } + doc.finishPage(page) + FileOutputStream(outFile).use { doc.writeTo(it) } + doc.close() +} + +fun writeTrainingDaySummaryPdf( + outFile: File, + clubName: String, + dateLabel: String, + timeLabel: String, + activeMembers: List, + participants: List, + freeform: List, + planItems: List, + timeblockFallback: String, +) { + val doc = PdfDocument() + var pageSeq = 0 + fun openPage(): PdfDocument.Page { + pageSeq++ + return doc.startPage(PdfDocument.PageInfo.Builder(PAGE_W, PAGE_H, pageSeq).create()) + } + var page = openPage() + var canvas = page.canvas + var y = MARGIN + val titlePaint = newTitlePaint() + val bodyPaint = newBodyPaint() + fun newPageIfNeeded(extra: Float) { + if (y + extra > PAGE_H - MARGIN) { + doc.finishPage(page) + page = openPage() + canvas = page.canvas + y = MARGIN + } + } + y = canvas.drawStatic("Trainingstag – $clubName", titlePaint, MARGIN, y) + y = canvas.drawStatic("Datum: $dateLabel", bodyPaint, MARGIN, y) + y = canvas.drawStatic("Zeiten: $timeLabel", bodyPaint, MARGIN, y) + y = canvas.drawStatic("", bodyPaint, MARGIN, y) + + y = canvas.drawStatic("Teilnehmer (laut App-Stand)", titlePaint, MARGIN, y) + val partLines = activeMembers.map { m -> + val row = participants.find { it.memberId == m.id } + val base = "${m.lastName}, ${m.firstName}" + if (row == null) { + "$base — (keine Teilnahme-Zeile)" + } else if (row.isPresentParticipant()) { + "$base — anwesend" + } else { + when (row.attendanceStatus?.lowercase()) { + "excused" -> "$base — entschuldigt" + "cancelled" -> "$base — abgesagt" + else -> "$base — (${row.attendanceStatus ?: "?"})" + } + } + } + if (partLines.isEmpty()) { + y = canvas.drawStatic("—", bodyPaint, MARGIN, y) + } else { + partLines.forEach { n -> + newPageIfNeeded(30f) + y = canvas.drawStatic("• $n", bodyPaint, MARGIN, y) + } + } + + y = canvas.drawStatic("", bodyPaint, MARGIN, y) + y = canvas.drawStatic("Weitere Tages-Aktivitäten", titlePaint, MARGIN, y) + if (freeform.isEmpty()) { + y = canvas.drawStatic("—", bodyPaint, MARGIN, y) + } else { + freeform.forEach { f -> + newPageIfNeeded(30f) + y = canvas.drawStatic("• ${f.description}", bodyPaint, MARGIN, y) + } + } + + y = canvas.drawStatic("", bodyPaint, MARGIN, y) + y = canvas.drawStatic("Trainingsplan (Aktivitäten)", titlePaint, MARGIN, y) + val sorted = planItems.sortedWith(compareBy({ it.groupId ?: 0 }, { it.orderId }, { it.id })) + if (sorted.isEmpty()) { + y = canvas.drawStatic("—", bodyPaint, MARGIN, y) + } else { + sorted.forEach { item -> + newPageIfNeeded(40f) + val line = buildString { + append(item.displayTitle(timeblockFallback).ifBlank { "Eintrag ${item.id}" }) + item.durationText?.takeIf { it.isNotBlank() }?.let { append(" — $it") } + ?: item.duration?.let { append(" — ${it} min") } + } + y = canvas.drawStatic("• $line", bodyPaint, MARGIN, y) + } + } + + doc.finishPage(page) + FileOutputStream(outFile).use { doc.writeTo(it) } + doc.close() +} diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfShare.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfShare.kt new file mode 100644 index 00000000..d0e8b023 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/pdf/DiaryPdfShare.kt @@ -0,0 +1,22 @@ +package de.tt_tagebuch.app.pdf + +import android.content.Context +import android.content.Intent +import androidx.core.content.FileProvider +import de.tt_tagebuch.app.BuildConfig +import java.io.File + +fun sharePdfFile(context: Context, file: File, chooserTitle: String) { + val uri = FileProvider.getUriForFile( + context, + "${BuildConfig.APPLICATION_ID}.fileprovider", + file, + ) + val send = Intent(Intent.ACTION_SEND).apply { + type = "application/pdf" + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + val chooser = Intent.createChooser(send, chooserTitle).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(chooser) +} diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AppRoot.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AppRoot.kt new file mode 100644 index 00000000..44535683 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AppRoot.kt @@ -0,0 +1,4465 @@ +package de.tt_tagebuch.app.ui + +import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.result.PickVisualMediaRequest +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.AlertDialog +import androidx.compose.material.BottomNavigation +import androidx.compose.material.BottomNavigationItem +import androidx.compose.material.Button +import androidx.compose.material.Card +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.OutlinedButton +import androidx.compose.material.Checkbox +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.Divider +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Surface +import androidx.compose.material.Switch +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.BarChart +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.material.icons.filled.ExpandLess +import androidx.compose.material.icons.filled.ExpandMore +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.People +import androidx.compose.material.icons.filled.Settings +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.key +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import de.tt_tagebuch.app.AppDependencies +import de.tt_tagebuch.app.pdf.sharePdfFile +import de.tt_tagebuch.app.pdf.writeTrainingDaySummaryPdf +import de.tt_tagebuch.app.pdf.writeTrainingPlanPdf +import de.tt_tagebuch.shared.api.memberProfileImagePath +import de.tt_tagebuch.shared.api.toAbsoluteUrl +import de.tt_tagebuch.shared.api.models.MemberGroupPhotoDto +import de.tt_tagebuch.shared.api.models.canReadDiary +import de.tt_tagebuch.shared.api.models.canReadMembers +import de.tt_tagebuch.shared.api.models.canWriteDiary +import de.tt_tagebuch.shared.api.models.canWriteMembers +import de.tt_tagebuch.shared.api.models.mainActivityImagePath +import de.tt_tagebuch.shared.api.models.nestedActivityImagePath +import de.tt_tagebuch.shared.api.models.AccidentReportDto +import de.tt_tagebuch.shared.api.models.Club +import de.tt_tagebuch.shared.api.models.DiaryDate +import de.tt_tagebuch.shared.api.models.DiaryFreeformActivity +import de.tt_tagebuch.shared.api.models.AddDiaryPlanGroupActivityRequest +import de.tt_tagebuch.shared.api.models.CreateDiaryPlanActivityRequest +import de.tt_tagebuch.shared.api.models.DiaryDateActivityItem +import de.tt_tagebuch.shared.api.models.DiaryPlanGroup +import de.tt_tagebuch.shared.api.models.UpdateDiaryPlanActivityRequest +import de.tt_tagebuch.shared.api.models.DiaryMemberNoteDto +import de.tt_tagebuch.shared.api.models.DiaryMemberTagLinkDto +import de.tt_tagebuch.shared.api.models.DiaryTag +import de.tt_tagebuch.shared.api.models.PredefinedActivityDto +import de.tt_tagebuch.shared.api.models.displayLabel as predefinedDtoDisplayLabel +import de.tt_tagebuch.shared.api.models.tagDefinitionId +import de.tt_tagebuch.shared.api.models.tagDisplayName +import de.tt_tagebuch.shared.api.models.DiaryTrainingParticipant +import de.tt_tagebuch.shared.api.models.isPresentParticipant +import de.tt_tagebuch.shared.api.models.displayLabel +import de.tt_tagebuch.shared.api.models.displayTitle +import de.tt_tagebuch.shared.api.models.memberLabel +import de.tt_tagebuch.shared.api.models.MemberActivityStatDto +import de.tt_tagebuch.shared.api.models.MemberContactSetBody +import de.tt_tagebuch.shared.api.models.MemberLastParticipationDto +import de.tt_tagebuch.shared.api.models.TrainingGroupDto +import de.tt_tagebuch.shared.api.models.TrainingTimeDto +import de.tt_tagebuch.shared.api.models.toSetBody +import de.tt_tagebuch.shared.api.models.Member +import de.tt_tagebuch.shared.i18n.MobileStrings +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import java.io.File + +private val LocalLanguageCode = compositionLocalOf { MobileStrings.DEFAULT_LANGUAGE } + +/** Ab dieser Fensterbreite (dp): seitliche Navigation wie auf Tablet/Web. */ +private const val MAIN_NAV_RAIL_MIN_WIDTH_DP = 600 + +private val ScreenHorizontalPadding = 20.dp +private val TouchMinHeight = 48.dp + +private const val AUTH_ROUTE_LOGIN = "login" +private const val AUTH_ROUTE_REGISTER = "register" +private const val AUTH_ROUTE_FORGOT = "forgot" +private const val AUTH_ROUTE_RESET = "reset" +private const val AUTH_ROUTE_ACTIVATE = "activate" + +private enum class MainTab { + Home, + Diary, + Members, + Stats, + Settings, +} + +@Composable +fun AppRoot(dependencies: AppDependencies) { + val authState by dependencies.authManager.state.collectAsState() + val clubState by dependencies.clubManager.state.collectAsState() + val languageState by dependencies.languageManager.state.collectAsState() + + LaunchedEffect(Unit) { + dependencies.languageManager.hydrate() + dependencies.authManager.hydrate() + dependencies.clubManager.hydrate() + } + LaunchedEffect(Unit) { + dependencies.unauthorizedEvents.collectLatest { count -> + if (count > 0) { + dependencies.authManager.clearLocal() + dependencies.clubManager.clearSelection() + dependencies.diaryManager.clear() + dependencies.membersManager.clear() + dependencies.trainingStatsManager.clear() + } + } + } + + CompositionLocalProvider(LocalLanguageCode provides languageState.currentLanguageCode) { + Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { + when { + authState.isHydrating -> LoadingScreen(tr("mobile.appLoading", "App wird geladen")) + !authState.isLoggedIn -> AuthFlowHost(dependencies = dependencies) + clubState.currentClubId == null -> ClubSelectScreen(dependencies) + else -> MainTabs(dependencies) + } + } + } +} + +@Composable +private fun MainTabs(dependencies: AppDependencies) { + var selectedTab by rememberSaveable { mutableStateOf(MainTab.Home) } + var diarySelectedEntryId by remember { mutableStateOf(null) } + var membersNestedOpen by remember { mutableStateOf(false) } + val useWideMainNav = LocalConfiguration.current.screenWidthDp >= MAIN_NAV_RAIL_MIN_WIDTH_DP + + val isNestedDetail = when (selectedTab) { + MainTab.Diary -> diarySelectedEntryId != null + MainTab.Members -> membersNestedOpen + else -> false + } + + fun selectMainTab(tab: MainTab) { + if (tab != selectedTab) { + diarySelectedEntryId = null + membersNestedOpen = false + } + selectedTab = tab + } + + if (useWideMainNav) { + Row(modifier = Modifier.fillMaxSize()) { + if (!isNestedDetail) { + MainNavigationRail( + selectedTab = selectedTab, + onTabSelected = { selectMainTab(it) }, + ) + Divider( + color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f), + modifier = Modifier.fillMaxHeight().width(1.dp), + ) + } + Box(modifier = Modifier.weight(1f).fillMaxHeight()) { + MainTabContent( + selectedTab = selectedTab, + dependencies = dependencies, + onNavigateTab = { selectMainTab(it) }, + diarySelectedEntryId = diarySelectedEntryId, + onDiarySelectedEntryId = { diarySelectedEntryId = it }, + onMembersNestedOpenChange = { membersNestedOpen = it }, + ) + } + } + } else { + Column(modifier = Modifier.fillMaxSize()) { + Box(modifier = Modifier.weight(1f).fillMaxWidth()) { + MainTabContent( + selectedTab = selectedTab, + dependencies = dependencies, + onNavigateTab = { selectMainTab(it) }, + diarySelectedEntryId = diarySelectedEntryId, + onDiarySelectedEntryId = { diarySelectedEntryId = it }, + onMembersNestedOpenChange = { membersNestedOpen = it }, + ) + } + if (!isNestedDetail) { + BottomNavigation( + backgroundColor = MaterialTheme.colors.surface, + contentColor = MaterialTheme.colors.onSurface, + elevation = 8.dp, + ) { + MainTab.values().forEach { tab -> + BottomNavigationItem( + icon = { Icon(mainTabIcon(tab), contentDescription = tabTitle(tab)) }, + label = { Text(tabTitle(tab)) }, + selected = selectedTab == tab, + alwaysShowLabel = true, + selectedContentColor = MaterialTheme.colors.primary, + unselectedContentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.55f), + onClick = { selectMainTab(tab) }, + ) + } + } + } + } + } +} + +@Composable +private fun MainTabContent( + selectedTab: MainTab, + dependencies: AppDependencies, + onNavigateTab: (MainTab) -> Unit, + diarySelectedEntryId: Int?, + onDiarySelectedEntryId: (Int?) -> Unit, + onMembersNestedOpenChange: (Boolean) -> Unit, +) { + when (selectedTab) { + MainTab.Home -> HomeScreen( + dependencies = dependencies, + onOpenTab = onNavigateTab, + ) + MainTab.Diary -> DiaryListScreen( + dependencies = dependencies, + selectedEntryId = diarySelectedEntryId, + onSelectedEntryId = onDiarySelectedEntryId, + ) + MainTab.Members -> MembersScreen( + dependencies = dependencies, + onNestedOpenChange = onMembersNestedOpenChange, + ) + MainTab.Stats -> TrainingStatsScreen(dependencies) + MainTab.Settings -> SettingsScreen(dependencies) + } +} + +@Composable +private fun HomeScreen( + dependencies: AppDependencies, + onOpenTab: (MainTab) -> Unit, +) { + val clubState by dependencies.clubManager.state.collectAsState() + val clubId = clubState.currentClubId ?: return + val clubName = remember(clubState.clubs, clubId) { + clubState.clubs.find { it.id == clubId }?.name ?: "Verein" + } + var clubDetail by remember { mutableStateOf(null) } + var detailError by remember { mutableStateOf(null) } + var detailLoading by remember { mutableStateOf(false) } + LaunchedEffect(clubId) { + detailLoading = true + detailError = null + clubDetail = runCatching { dependencies.clubManager.fetchClubDetail(clubId) } + .onFailure { detailError = it.message } + .getOrNull() + detailLoading = false + } + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp) + .verticalScroll(rememberScrollState()), + ) { + Header(tr("home.welcomeShort", "Willkommen")) + Text( + clubName, + style = MaterialTheme.typography.subtitle1, + fontWeight = FontWeight.SemiBold, + modifier = Modifier.padding(bottom = 8.dp), + ) + Text( + tr("home.mobileHubHint", "Schnellzugriff auf die Hauptbereiche deines Vereins."), + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(bottom = 16.dp), + ) + HomeHubTile( + title = tr("navigation.diary", "Tagebuch"), + subtitle = tr("home.tileDiary", "Trainingstage, Plan, Teilnehmer"), + onClick = { onOpenTab(MainTab.Diary) }, + ) + HomeHubTile( + title = tr("navigation.members", "Mitglieder"), + subtitle = tr("home.tileMembers", "Liste und Profile"), + onClick = { onOpenTab(MainTab.Members) }, + ) + HomeHubTile( + title = tr("navigation.statistics", "Statistik"), + subtitle = tr("home.tileStats", "Kennzahlen und Teilnahmen"), + onClick = { onOpenTab(MainTab.Stats) }, + ) + HomeHubTile( + title = tr("mobile.more", "Mehr"), + subtitle = tr("home.tileSettings", "Sprache, Session, Links"), + onClick = { onOpenTab(MainTab.Settings) }, + ) + SectionTitle(tr("home.clubSection", "Verein")) + ErrorText(detailError) + when { + detailLoading -> LoadingInline() + clubDetail != null -> { + val c = clubDetail!! + Card(modifier = Modifier.fillMaxWidth(), elevation = 1.dp) { + Column(modifier = Modifier.padding(12.dp)) { + DetailLine(tr("club.name", "Name"), c.name) + c.greetingText?.takeIf { it.isNotBlank() }?.let { + Text(it, style = MaterialTheme.typography.body2, modifier = Modifier.padding(top = 8.dp)) + } + c.associationMemberNumber?.takeIf { it.isNotBlank() }?.let { + DetailLine(tr("club.associationNumber", "Verbands-Nr."), it) + } + c.myTischtennisFedNickname?.takeIf { it.isNotBlank() }?.let { + DetailLine("MyTischtennis", it) + } + } + } + } + } + } +} + +@Composable +private fun HomeHubTile(title: String, subtitle: String, onClick: () -> Unit) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + .clickable(onClick = onClick), + elevation = 2.dp, + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text(title, style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold) + Text( + subtitle, + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f), + modifier = Modifier.padding(top = 4.dp), + ) + } + } +} + +@Composable +private fun MainNavigationRail( + selectedTab: MainTab, + onTabSelected: (MainTab) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .width(100.dp) + .fillMaxHeight() + .background(TtAppColors.NavRailBackground) + .padding(vertical = 12.dp, horizontal = 8.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + MainTab.values().forEach { tab -> + val selected = selectedTab == tab + Surface( + modifier = Modifier + .fillMaxWidth() + .clickable { onTabSelected(tab) }, + color = if (selected) { + MaterialTheme.colors.primary.copy(alpha = 0.12f) + } else { + MaterialTheme.colors.surface + }, + elevation = 0.dp, + ) { + Column( + modifier = Modifier + .padding(vertical = 12.dp, horizontal = 4.dp) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + imageVector = mainTabIcon(tab), + contentDescription = tabTitle(tab), + tint = if (selected) { + MaterialTheme.colors.primary + } else { + MaterialTheme.colors.onSurface.copy(alpha = 0.6f) + }, + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = tabTitle(tab), + style = MaterialTheme.typography.caption, + color = if (selected) { + MaterialTheme.colors.primary + } else { + MaterialTheme.colors.onSurface.copy(alpha = 0.87f) + }, + ) + } + } + } + } +} + +private fun mainTabIcon(tab: MainTab): ImageVector = when (tab) { + MainTab.Home -> Icons.Filled.Home + MainTab.Diary -> Icons.Filled.DateRange + MainTab.Members -> Icons.Filled.People + MainTab.Stats -> Icons.Filled.BarChart + MainTab.Settings -> Icons.Filled.Settings +} + +@Composable +private fun ScreenTopBar( + title: String, + onBack: () -> Unit, + hint: String? = null, +) { + val backLabel = tr("common.back", "Zurück") + Column(modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 56.dp) + .padding(vertical = 2.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + IconButton( + onClick = onBack, + modifier = Modifier.size(48.dp), + ) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = backLabel) + } + Text(title, style = MaterialTheme.typography.h6, modifier = Modifier.weight(1f)) + } + hint?.let { + Text( + it, + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f), + modifier = Modifier + .fillMaxWidth() + .padding(start = 4.dp, end = 4.dp, bottom = 8.dp), + ) + } + } +} + +@Composable +private fun AuthFlowHost(dependencies: AppDependencies) { + var route by rememberSaveable { mutableStateOf(AUTH_ROUTE_LOGIN) } + val goLogin = { route = AUTH_ROUTE_LOGIN } + when (route) { + AUTH_ROUTE_REGISTER -> RegisterScreen(dependencies = dependencies, onBack = goLogin) + AUTH_ROUTE_FORGOT -> ForgotPasswordScreen(dependencies = dependencies, onBack = goLogin) + AUTH_ROUTE_RESET -> ResetPasswordScreen(dependencies = dependencies, onBack = goLogin) + AUTH_ROUTE_ACTIVATE -> ActivateAccountScreen(dependencies = dependencies, onBack = goLogin) + else -> LoginScreen( + dependencies = dependencies, + onLogin = { email, password -> + dependencies.authManager.login(email, password) + dependencies.clubManager.loadClubs() + }, + onRegister = { route = AUTH_ROUTE_REGISTER }, + onForgotPassword = { route = AUTH_ROUTE_FORGOT }, + onResetPasswordWithToken = { route = AUTH_ROUTE_RESET }, + onActivateAccount = { route = AUTH_ROUTE_ACTIVATE }, + ) + } +} + +@Composable +private fun LoginScreen( + dependencies: AppDependencies, + onLogin: suspend (String, String) -> Unit, + onRegister: () -> Unit = {}, + onForgotPassword: () -> Unit = {}, + onResetPasswordWithToken: () -> Unit = {}, + onActivateAccount: () -> Unit = {}, +) { + var email by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var error by rememberSaveable { mutableStateOf(null) } + var isLoading by rememberSaveable { mutableStateOf(false) } + val requiredMessage = tr("mobile.emailAndPasswordRequired", "E-Mail und Passwort sind erforderlich") + val loginFailedMessage = tr("mobile.loginFailed", "Login fehlgeschlagen") + + FormScaffold( + title = tr("app.title", "Trainingstagebuch"), + subtitle = tr("mobile.loginSubtitle", "Melde dich mit E-Mail und Passwort an – wie in der Web-App."), + ) { + OutlinedTextField( + value = email, + onValueChange = { email = it }, + label = { Text("E-Mail") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Passwort") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + visualTransformation = PasswordVisualTransformation(), + ) + ErrorText(error) + Button( + onClick = { + if (email.isBlank() || password.isBlank()) { + error = requiredMessage + return@Button + } + isLoading = true + error = null + dependencies.applicationScope.launch { + runCatching { onLogin(email.trim(), password) } + .onFailure { error = it.message ?: loginFailedMessage } + isLoading = false + } + }, + enabled = !isLoading, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(if (isLoading) tr("mobile.loginInProgress", "Anmelden...") else tr("auth.login", "Login")) + } + TextButton(onClick = onRegister, modifier = Modifier.fillMaxWidth()) { + Text(tr("auth.register", "Registrieren")) + } + TextButton(onClick = onForgotPassword, modifier = Modifier.fillMaxWidth()) { + Text(tr("auth.forgotPassword", "Passwort vergessen")) + } + TextButton(onClick = onResetPasswordWithToken, modifier = Modifier.fillMaxWidth()) { + Text(tr("mobile.resetPasswordWithToken", "Neues Passwort (Code aus E-Mail)")) + } + TextButton(onClick = onActivateAccount, modifier = Modifier.fillMaxWidth()) { + Text(tr("mobile.activateAccount", "Account aktivieren")) + } + } +} + +@Composable +private fun RegisterScreen(dependencies: AppDependencies, onBack: () -> Unit) { + var email by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var error by rememberSaveable { mutableStateOf(null) } + var isLoading by rememberSaveable { mutableStateOf(false) } + var success by rememberSaveable { mutableStateOf(false) } + val scroll = rememberScrollState() + val emailPwdRequired = tr("mobile.emailAndPasswordRequired", "E-Mail und Passwort sind erforderlich") + val registerFailed = tr("auth.registerFailed", "Registrierung fehlgeschlagen") + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scroll) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp), + ) { + ScreenTopBar(title = tr("auth.register", "Registrieren"), onBack = onBack) + Spacer(modifier = Modifier.height(16.dp)) + if (!success) { + OutlinedTextField( + value = email, + onValueChange = { email = it }, + label = { Text("E-Mail") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Passwort") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + visualTransformation = PasswordVisualTransformation(), + ) + ErrorText(error) + Button( + onClick = { + if (email.isBlank() || password.isBlank()) { + error = emailPwdRequired + return@Button + } + isLoading = true + error = null + dependencies.applicationScope.launch { + runCatching { dependencies.publicAuthApi.register(email.trim(), password) } + .onSuccess { success = true } + .onFailure { error = it.message ?: registerFailed } + isLoading = false + } + }, + enabled = !isLoading, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(if (isLoading) "…" else tr("auth.register", "Registrieren")) + } + } else { + Text( + tr("mobile.registerCheckEmail", "Bitte bestätige deine E-Mail-Adresse über den Link in der Nachricht."), + modifier = Modifier.padding(vertical = 8.dp), + ) + Button( + onClick = onBack, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("auth.toLogin", "Zum Login")) + } + } + } +} + +@Composable +private fun ForgotPasswordScreen(dependencies: AppDependencies, onBack: () -> Unit) { + var email by rememberSaveable { mutableStateOf("") } + var error by rememberSaveable { mutableStateOf(null) } + var isLoading by rememberSaveable { mutableStateOf(false) } + var sent by rememberSaveable { mutableStateOf(false) } + val scroll = rememberScrollState() + val emailRequired = tr("mobile.emailRequired", "E-Mail ist erforderlich") + val resetRequestFailed = tr("auth.resetRequestFailed", "Anfrage fehlgeschlagen") + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scroll) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp), + ) { + ScreenTopBar(title = tr("auth.forgotPassword", "Passwort vergessen"), onBack = onBack) + Spacer(modifier = Modifier.height(16.dp)) + if (!sent) { + Text(tr("auth.forgotPasswordDescription", "Wir senden dir einen Link zum Zurücksetzen."), modifier = Modifier.padding(bottom = 8.dp)) + OutlinedTextField( + value = email, + onValueChange = { email = it }, + label = { Text("E-Mail") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + ErrorText(error) + Button( + onClick = { + if (email.isBlank()) { + error = emailRequired + return@Button + } + isLoading = true + error = null + dependencies.applicationScope.launch { + runCatching { dependencies.publicAuthApi.forgotPassword(email.trim()) } + .onSuccess { sent = true } + .onFailure { error = it.message ?: resetRequestFailed } + isLoading = false + } + }, + enabled = !isLoading, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(if (isLoading) tr("auth.sending", "Senden…") else tr("auth.sendResetLink", "Link senden")) + } + } else { + Text(tr("auth.resetEmailSent", "Wenn ein Konto existiert, erhältst du eine E-Mail."), modifier = Modifier.padding(vertical = 8.dp)) + Button( + onClick = onBack, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("auth.toLogin", "Zum Login")) + } + } + } +} + +@Composable +private fun ResetPasswordScreen(dependencies: AppDependencies, onBack: () -> Unit) { + var token by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var password2 by rememberSaveable { mutableStateOf("") } + var error by rememberSaveable { mutableStateOf(null) } + var isLoading by rememberSaveable { mutableStateOf(false) } + var success by rememberSaveable { mutableStateOf(false) } + val scroll = rememberScrollState() + val tokenPwdRequired = tr("mobile.tokenAndPasswordRequired", "Code und Passwort sind erforderlich") + val passwordMismatch = tr("mobile.passwordMismatch", "Passwörter stimmen nicht überein") + val resetFailed = tr("mobile.resetFailed", "Zurücksetzen fehlgeschlagen") + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scroll) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp), + ) { + ScreenTopBar(title = tr("mobile.resetPasswordTitle", "Neues Passwort setzen"), onBack = onBack) + Spacer(modifier = Modifier.height(16.dp)) + if (!success) { + OutlinedTextField( + value = token, + onValueChange = { token = it }, + label = { Text(tr("mobile.resetToken", "Code aus der E-Mail")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text(tr("auth.password", "Neues Passwort")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + visualTransformation = PasswordVisualTransformation(), + ) + OutlinedTextField( + value = password2, + onValueChange = { password2 = it }, + label = { Text(tr("mobile.passwordRepeat", "Passwort wiederholen")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + visualTransformation = PasswordVisualTransformation(), + ) + ErrorText(error) + Button( + onClick = { + if (token.isBlank() || password.isBlank()) { + error = tokenPwdRequired + return@Button + } + if (password != password2) { + error = passwordMismatch + return@Button + } + isLoading = true + error = null + dependencies.applicationScope.launch { + runCatching { dependencies.publicAuthApi.resetPassword(token.trim(), password) } + .onSuccess { success = true } + .onFailure { error = it.message ?: resetFailed } + isLoading = false + } + }, + enabled = !isLoading, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("mobile.savePassword", "Passwort speichern")) + } + } else { + Text(tr("mobile.passwordChanged", "Passwort geändert. Bitte neu anmelden."), modifier = Modifier.padding(vertical = 8.dp)) + Button( + onClick = onBack, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("auth.toLogin", "Zum Login")) + } + } + } +} + +@Composable +private fun ActivateAccountScreen(dependencies: AppDependencies, onBack: () -> Unit) { + var code by rememberSaveable { mutableStateOf("") } + var error by rememberSaveable { mutableStateOf(null) } + var isLoading by rememberSaveable { mutableStateOf(false) } + var success by rememberSaveable { mutableStateOf(false) } + val scroll = rememberScrollState() + val codeRequired = tr("mobile.codeRequired", "Code ist erforderlich") + val activateFailed = tr("mobile.activateFailed", "Aktivierung fehlgeschlagen") + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scroll) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp), + ) { + ScreenTopBar(title = tr("mobile.activateAccount", "Account aktivieren"), onBack = onBack) + Spacer(modifier = Modifier.height(16.dp)) + if (!success) { + OutlinedTextField( + value = code, + onValueChange = { code = it }, + label = { Text(tr("mobile.activationCode", "Aktivierungscode")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + ErrorText(error) + Button( + onClick = { + if (code.isBlank()) { + error = codeRequired + return@Button + } + isLoading = true + error = null + dependencies.applicationScope.launch { + runCatching { dependencies.publicAuthApi.activate(code.trim()) } + .onSuccess { success = true } + .onFailure { error = it.message ?: activateFailed } + isLoading = false + } + }, + enabled = !isLoading, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("mobile.activateSubmit", "Aktivieren")) + } + } else { + Text(tr("mobile.activateSuccess", "Account aktiviert. Du kannst dich anmelden."), modifier = Modifier.padding(vertical = 8.dp)) + Button( + onClick = onBack, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("auth.toLogin", "Zum Login")) + } + } + } +} + +@Composable +private fun ClubSelectScreen(dependencies: AppDependencies) { + val clubState by dependencies.clubManager.state.collectAsState() + var requestedClubId by rememberSaveable { mutableStateOf(null) } + var showCreateClub by rememberSaveable { mutableStateOf(false) } + var newClubName by rememberSaveable { mutableStateOf("") } + + LaunchedEffect(Unit) { + dependencies.clubManager.loadClubs() + } + + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp), + ) { + Header(tr("club.select", "Club auswählen")) + Text( + tr("club.selectHelp", "Tippe auf „Auswählen“, um mit diesem Verein zu arbeiten. Ohne Zugriff kannst du eine Anfrage stellen."), + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(bottom = 12.dp), + ) + OutlinedButton( + onClick = { showCreateClub = !showCreateClub }, + enabled = !clubState.isLoading, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(if (showCreateClub) tr("mobile.cancel", "Abbrechen") else tr("club.createNew", "Neuen Verein anlegen")) + } + if (showCreateClub) { + OutlinedTextField( + value = newClubName, + onValueChange = { newClubName = it }, + label = { Text(tr("club.newName", "Name des Vereins")) }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + singleLine = true, + enabled = !clubState.isLoading, + ) + Button( + onClick = { + val n = newClubName.trim() + if (n.isEmpty()) return@Button + dependencies.applicationScope.launch { + dependencies.clubManager.createClub(n) + newClubName = "" + showCreateClub = false + } + }, + enabled = !clubState.isLoading && newClubName.isNotBlank(), + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("club.createSubmit", "Verein erstellen")) } + } + if (clubState.isLoading) LoadingInline() + ErrorText(clubState.error) + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + items(clubState.clubs) { club -> + Card(modifier = Modifier.fillMaxWidth(), elevation = 2.dp) { + Column(modifier = Modifier.padding(16.dp)) { + Text(club.name, style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold) + Spacer(modifier = Modifier.height(10.dp)) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + Button( + onClick = { dependencies.applicationScope.launch { dependencies.clubManager.selectClub(club.id) } }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { + Text(tr("mobile.select", "Auswählen")) + } + OutlinedButton( + onClick = { + dependencies.applicationScope.launch { + dependencies.clubManager.requestAccess(club.id) + requestedClubId = club.id + } + }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { + Text( + if (requestedClubId == club.id) tr("mobile.clubRequested", "Angefragt") else tr("mobile.clubRequest", "Zugriff anfragen"), + maxLines = 2, + ) + } + } + } + } + } + } + Spacer(modifier = Modifier.height(12.dp)) + OutlinedButton( + onClick = { dependencies.applicationScope.launch { dependencies.authManager.logout() } }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("auth.logout", "Abmelden")) + } + } +} + +@Composable +private fun DiaryListScreen( + dependencies: AppDependencies, + selectedEntryId: Int?, + onSelectedEntryId: (Int?) -> Unit, +) { + val clubState by dependencies.clubManager.state.collectAsState() + val diaryState by dependencies.diaryManager.state.collectAsState() + val clubId = clubState.currentClubId ?: return + var showCreate by rememberSaveable { mutableStateOf(false) } + + LaunchedEffect(clubId) { + onSelectedEntryId(null) + dependencies.diaryManager.loadDates(clubId) + } + + val selectedEntry = diaryState.dates.firstOrNull { it.id == selectedEntryId } + if (selectedEntry != null) { + DiaryDetailScreen( + clubId = clubId, + entry = selectedEntry, + dependencies = dependencies, + onBack = { onSelectedEntryId(null) }, + ) + return + } + + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp), + ) { + Header(tr("diary.title", "Tagebuch")) + Text( + tr("diary.listHint", "Tippe auf einen Tag für Plan, Teilnehmer und Notizen."), + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(bottom = 12.dp), + ) + Row(horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedButton( + onClick = { dependencies.applicationScope.launch { dependencies.diaryManager.loadDates(clubId) } }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { + Text(tr("mobile.refresh", "Aktualisieren")) + } + Button( + onClick = { showCreate = !showCreate }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { + Text(if (showCreate) tr("mobile.cancel", "Abbrechen") else tr("mobile.new", "Neuer Tag")) + } + } + if (showCreate) { + DiaryEditForm( + submitLabel = tr("mobile.createDiaryEntry", "Eintrag erstellen"), + onSubmit = { date, start, end -> + dependencies.applicationScope.launch { + dependencies.diaryManager.createDate(clubId, date, start, end) + showCreate = false + } + }, + ) + } + if (diaryState.isLoading) LoadingInline() + ErrorText(diaryState.error) + if (!diaryState.isLoading && diaryState.dates.isEmpty()) { + EmptyText(tr("mobile.noDiaryEntries", "Noch keine Tagebuch-Einträge")) + } + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + items(diaryState.dates) { entry -> + DiaryListItem(entry = entry, onClick = { onSelectedEntryId(entry.id) }) + } + } + } +} + +@Composable +private fun DiaryDetailScreen( + clubId: Int, + entry: DiaryDate, + dependencies: AppDependencies, + onBack: () -> Unit, +) { + BackHandler(onBack = onBack) + + val diaryState by dependencies.diaryManager.state.collectAsState() + val membersState by dependencies.membersManager.state.collectAsState() + val clubState by dependencies.clubManager.state.collectAsState() + val canWriteDiary = clubState.currentPermissions?.canWriteDiary() == true + val canReadDiary = clubState.currentPermissions?.canReadDiary() == true + val canReadMembers = clubState.currentPermissions?.canReadMembers() == true + val canWriteMembers = clubState.currentPermissions?.canWriteMembers() == true + var showEdit by rememberSaveable { mutableStateOf(false) } + var note by rememberSaveable { mutableStateOf("") } + var tagName by rememberSaveable { mutableStateOf("") } + var planItems by remember { mutableStateOf>(emptyList()) } + var planLoading by remember { mutableStateOf(false) } + var planError by remember { mutableStateOf(null) } + var planGroups by remember { mutableStateOf>(emptyList()) } + var planMutating by remember { mutableStateOf(false) } + var planActionError by remember { mutableStateOf(null) } + var showAddPlanActivity by rememberSaveable { mutableStateOf(false) } + var showAddPlanGroupActivity by rememberSaveable { mutableStateOf(false) } + var showAddTrainingGroup by rememberSaveable { mutableStateOf(false) } + var newPlanActivityName by rememberSaveable { mutableStateOf("") } + var newPlanDuration by rememberSaveable { mutableStateOf("") } + var newPlanDurationText by rememberSaveable { mutableStateOf("") } + var newPlanIsTimeblock by rememberSaveable { mutableStateOf(false) } + var newPlanTargetGroupId by rememberSaveable { mutableStateOf(null) } + var newPlanGroupActivityGroupId by rememberSaveable { mutableStateOf(null) } + var newTrainingGroupName by rememberSaveable { mutableStateOf("") } + var newTrainingGroupLead by rememberSaveable { mutableStateOf("") } + var newScopedActivityName by rememberSaveable { mutableStateOf("") } + var newScopedDuration by rememberSaveable { mutableStateOf("") } + var newScopedDurationText by rememberSaveable { mutableStateOf("") } + var editingPlanItem by remember { mutableStateOf(null) } + var editPlanName by remember { mutableStateOf("") } + var editPlanDuration by remember { mutableStateOf("") } + var editPlanDurationText by remember { mutableStateOf("") } + var editPlanGroupId by remember { mutableStateOf(null) } + var participants by remember { mutableStateOf>(emptyList()) } + var participantsLoading by remember { mutableStateOf(false) } + var participantsError by remember { mutableStateOf(null) } + var participantMutating by remember { mutableStateOf(false) } + var participantGroupMenuMemberId by remember { mutableStateOf(null) } + var participantsSectionExpanded by rememberSaveable { mutableStateOf(false) } + var accidents by remember { mutableStateOf>(emptyList()) } + var accidentSectionError by remember { mutableStateOf(null) } + var newAccidentMemberId by rememberSaveable { mutableStateOf(null) } + var newAccidentDescription by rememberSaveable { mutableStateOf("") } + var accidentMemberMenu by remember { mutableStateOf(false) } + var accidentSubmitBusy by remember { mutableStateOf(false) } + var groupPhotos by remember { mutableStateOf>(emptyList()) } + var gallerySectionExpanded by rememberSaveable { mutableStateOf(false) } + var planImageViewerUrl by remember { mutableStateOf(null) } + var newGroupPhotoTitle by rememberSaveable { mutableStateOf("") } + var newGroupPhotoDescription by rememberSaveable { mutableStateOf("") } + var groupPhotoBusy by remember { mutableStateOf(false) } + var freeformActivities by remember { mutableStateOf>(emptyList()) } + var newFreeformDescription by rememberSaveable { mutableStateOf("") } + var freeformBusy by remember { mutableStateOf(false) } + var expandedMemberAssignActivityId by remember { mutableStateOf(null) } + var memberAssignMap by remember { mutableStateOf>>(emptyMap()) } + var memberAssignBusy by remember { mutableStateOf(false) } + var memberNotesSheetMember by remember { mutableStateOf(null) } + var memberNotesList by remember { mutableStateOf>(emptyList()) } + var memberTagsList by remember { mutableStateOf>(emptyList()) } + var memberNotesAvailableTags by remember { mutableStateOf>(emptyList()) } + var memberNotesBusy by remember { mutableStateOf(false) } + var memberNotesError by remember { mutableStateOf(null) } + var newMemberContextNote by rememberSaveable { mutableStateOf("") } + var newMemberContextTagName by rememberSaveable { mutableStateOf("") } + var memberTagPickMenu by remember { mutableStateOf(false) } + var newPlanPredefQuery by rememberSaveable { mutableStateOf("") } + var newPlanPredefResults by remember { mutableStateOf>(emptyList()) } + var newPlanPredefLoading by remember { mutableStateOf(false) } + var newPlanPickedPredefined by remember { mutableStateOf(null) } + var newPlanStandardPredefExpanded by rememberSaveable { mutableStateOf(false) } + var newPlanStandardPredefList by remember { mutableStateOf>(emptyList()) } + var scopedPredefQuery by rememberSaveable { mutableStateOf("") } + var scopedPredefResults by remember { mutableStateOf>(emptyList()) } + var scopedPredefLoading by remember { mutableStateOf(false) } + var scopedPickedPredefined by remember { mutableStateOf(null) } + var scopedStandardPredefExpanded by rememberSaveable { mutableStateOf(false) } + var scopedStandardPredefList by remember { mutableStateOf>(emptyList()) } + var editPlanPredefQuery by rememberSaveable { mutableStateOf("") } + var editPlanPredefResults by remember { mutableStateOf>(emptyList()) } + var editPlanPredefLoading by remember { mutableStateOf(false) } + var editPlanPickedPredefinedId by remember { mutableStateOf(null) } + val scroll = rememberScrollState() + val androidContext = LocalContext.current + val pickGroupPhoto = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> + if (uri == null) return@rememberLauncherForActivityResult + dependencies.applicationScope.launch { + groupPhotoBusy = true + try { + val bytes = androidContext.contentResolver.openInputStream(uri)?.use { it.readBytes() } ?: return@launch + dependencies.diaryManager.uploadMemberGroupPhoto( + clubId, + bytes, + newGroupPhotoTitle.ifBlank { "Gruppenfoto" }, + newGroupPhotoDescription, + ) + groupPhotos = dependencies.diaryManager.listMemberGroupPhotos(clubId) + } catch (_: Throwable) { + } finally { + groupPhotoBusy = false + } + } + } + + LaunchedEffect(clubId) { + dependencies.membersManager.loadMembers(clubId) + } + + LaunchedEffect(entry.id) { + expandedMemberAssignActivityId = null + memberAssignMap = emptyMap() + } + + LaunchedEffect(clubId, entry.id) { + planLoading = true + participantsLoading = true + planError = null + participantsError = null + try { + coroutineScope { + val activities = async { dependencies.diaryManager.fetchDateActivities(clubId, entry.id) } + val groups = async { runCatching { dependencies.diaryManager.listTrainingGroups(clubId, entry.id) }.getOrElse { emptyList() } } + val parts = async { dependencies.diaryManager.listTrainingParticipants(entry.id) } + val freeform = async { runCatching { dependencies.diaryManager.listFreeformActivities(entry.id) }.getOrElse { emptyList() } } + val accList = async { runCatching { dependencies.diaryManager.listAccidents(clubId, entry.id) } } + val photos = async { runCatching { dependencies.diaryManager.listMemberGroupPhotos(clubId) }.getOrElse { emptyList() } } + planItems = activities.await() + planGroups = groups.await() + participants = parts.await() + freeformActivities = freeform.await() + groupPhotos = photos.await() + accList.await().fold( + onSuccess = { + accidents = it + accidentSectionError = null + }, + onFailure = { t -> + accidents = emptyList() + accidentSectionError = t.message + }, + ) + } + } catch (t: Throwable) { + val msg = t.message ?: "Daten konnten nicht geladen werden" + planError = msg + participantsError = msg + planItems = emptyList() + planGroups = emptyList() + participants = emptyList() + freeformActivities = emptyList() + accidents = emptyList() + groupPhotos = emptyList() + } + planLoading = false + participantsLoading = false + } + + val refreshPlanAfterMutation: () -> Unit = { + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + coroutineScope { + val activities = async { dependencies.diaryManager.fetchDateActivities(clubId, entry.id) } + val groups = async { runCatching { dependencies.diaryManager.listTrainingGroups(clubId, entry.id) }.getOrElse { emptyList() } } + val parts = async { dependencies.diaryManager.listTrainingParticipants(entry.id) } + val freeform = async { runCatching { dependencies.diaryManager.listFreeformActivities(entry.id) }.getOrElse { emptyList() } } + val accList = async { runCatching { dependencies.diaryManager.listAccidents(clubId, entry.id) } } + val photos = async { runCatching { dependencies.diaryManager.listMemberGroupPhotos(clubId) }.getOrElse { emptyList() } } + planItems = activities.await() + planGroups = groups.await() + participants = parts.await() + freeformActivities = freeform.await() + groupPhotos = photos.await() + accList.await().fold( + onSuccess = { + accidents = it + accidentSectionError = null + }, + onFailure = { t -> + accidents = emptyList() + accidentSectionError = t.message + }, + ) + } + } catch (t: Throwable) { + planActionError = t.message ?: "Plan konnte nicht aktualisiert werden" + } finally { + planMutating = false + } + } + } + + LaunchedEffect(editingPlanItem?.id) { + val item = editingPlanItem ?: return@LaunchedEffect + editPlanName = item.predefinedActivity.displayLabel() + editPlanDuration = item.duration?.toString().orEmpty() + editPlanDurationText = item.durationText.orEmpty() + editPlanGroupId = item.groupId + editPlanPredefQuery = "" + editPlanPredefResults = emptyList() + editPlanPickedPredefinedId = null + } + + LaunchedEffect(memberNotesSheetMember?.id, entry.id, clubId) { + val m = memberNotesSheetMember ?: return@LaunchedEffect + memberNotesBusy = true + memberNotesError = null + try { + coroutineScope { + val n = async { dependencies.diaryManager.listMemberNotes(clubId, entry.id, m.id) } + val t = async { dependencies.diaryManager.listMemberTags(clubId, entry.id, m.id) } + val a = async { dependencies.diaryManager.listAllDiaryTags() } + memberNotesList = n.await() + memberTagsList = t.await() + memberNotesAvailableTags = a.await() + } + } catch (t: Throwable) { + memberNotesError = t.message ?: "Fehler beim Laden" + } finally { + memberNotesBusy = false + } + } + + LaunchedEffect(newPlanPredefQuery, showAddPlanActivity) { + if (!showAddPlanActivity) return@LaunchedEffect + delay(350) + val q = newPlanPredefQuery.trim() + if (q.length < 2) { + newPlanPredefResults = emptyList() + return@LaunchedEffect + } + newPlanPredefLoading = true + try { + newPlanPredefResults = dependencies.diaryManager.searchPredefinedActivities(q) + } catch (_: Throwable) { + newPlanPredefResults = emptyList() + } finally { + newPlanPredefLoading = false + } + } + + LaunchedEffect(newPlanStandardPredefExpanded, showAddPlanActivity) { + if (!showAddPlanActivity || !newPlanStandardPredefExpanded) return@LaunchedEffect + if (newPlanStandardPredefList.isNotEmpty()) return@LaunchedEffect + try { + newPlanStandardPredefList = dependencies.diaryManager.listPredefinedActivities("standard") + } catch (_: Throwable) { + newPlanStandardPredefList = emptyList() + } + } + + LaunchedEffect(scopedPredefQuery, showAddPlanGroupActivity) { + if (!showAddPlanGroupActivity) return@LaunchedEffect + delay(350) + val q = scopedPredefQuery.trim() + if (q.length < 2) { + scopedPredefResults = emptyList() + return@LaunchedEffect + } + scopedPredefLoading = true + try { + scopedPredefResults = dependencies.diaryManager.searchPredefinedActivities(q) + } catch (_: Throwable) { + scopedPredefResults = emptyList() + } finally { + scopedPredefLoading = false + } + } + + LaunchedEffect(scopedStandardPredefExpanded, showAddPlanGroupActivity) { + if (!showAddPlanGroupActivity || !scopedStandardPredefExpanded) return@LaunchedEffect + if (scopedStandardPredefList.isNotEmpty()) return@LaunchedEffect + try { + scopedStandardPredefList = dependencies.diaryManager.listPredefinedActivities("standard") + } catch (_: Throwable) { + scopedStandardPredefList = emptyList() + } + } + + LaunchedEffect(editPlanPredefQuery, editingPlanItem?.id) { + if (editingPlanItem == null) return@LaunchedEffect + delay(350) + val q = editPlanPredefQuery.trim() + if (q.length < 2) { + editPlanPredefResults = emptyList() + return@LaunchedEffect + } + editPlanPredefLoading = true + try { + editPlanPredefResults = dependencies.diaryManager.searchPredefinedActivities(q) + } catch (_: Throwable) { + editPlanPredefResults = emptyList() + } finally { + editPlanPredefLoading = false + } + } + + val activeMembers = remember(membersState.members) { + membersState.members.filter { it.active }.sortedWith( + compareBy({ it.lastName.lowercase() }, { it.firstName.lowercase() }), + ) + } + + val presentMembersForPlan = remember(activeMembers, participants) { + activeMembers.filter { m -> + participants.find { it.memberId == m.id }?.isPresentParticipant() == true + } + } + + val narrowPhone = LocalConfiguration.current.screenWidthDp < MAIN_NAV_RAIL_MIN_WIDTH_DP + + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 12.dp), + ) { + ScreenTopBar( + title = tr("mobile.entry", "Eintrag"), + onBack = onBack, + hint = if (narrowPhone) { + tr("mobile.detailNavHint", "Zurück: Pfeil oben links oder die System-Taste „Zurück“.") + } else { + null + }, + ) + Text(formatDate(entry.date), style = MaterialTheme.typography.subtitle1) + Text(formatTimeRange(entry.trainingStart, entry.trainingEnd)) + + Column( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .verticalScroll(scroll), + ) { + ErrorText(diaryState.error) + planError?.let { ErrorText(it) } + participantsError?.let { ErrorText(it) } + if (diaryState.isLoading || planLoading || participantsLoading) LoadingInline() + if (clubState.currentPermissions != null && !canWriteDiary) { + Text( + tr("diary.readOnlyHint", "Sie haben nur Lesezugriff auf das Tagebuch. Änderungen sind nicht möglich."), + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.75f), + modifier = Modifier.padding(bottom = 8.dp), + ) + } + + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + Button( + onClick = { showEdit = !showEdit }, + enabled = canWriteDiary, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(if (showEdit) tr("mobile.cancel", "Abbrechen") else tr("mobile.editTimes", "Zeiten bearbeiten")) } + OutlinedButton( + onClick = { dependencies.applicationScope.launch { dependencies.diaryManager.deleteDate(clubId, entry.id); onBack() } }, + enabled = canWriteDiary, + modifier = Modifier.heightIn(min = TouchMinHeight), + ) { + Text(tr("common.delete", "Löschen")) + } + } + if (showEdit) { + DiaryEditForm( + initialDate = entry.date.take(10), + initialStart = entry.trainingStart.orEmpty(), + initialEnd = entry.trainingEnd.orEmpty(), + submitLabel = tr("common.save", "Speichern"), + onSubmit = { _, start, end -> + dependencies.applicationScope.launch { + dependencies.diaryManager.updateTimes(clubId, entry.id, start, end) + showEdit = false + } + }, + ) + } + + OutlinedButton( + onClick = { participantsSectionExpanded = !participantsSectionExpanded }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp) + .heightIn(min = TouchMinHeight), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column(modifier = Modifier.weight(1f)) { + Text(tr("diary.participants", "Trainingsteilnehmer"), style = MaterialTheme.typography.subtitle1) + if (!participantsSectionExpanded) { + Text( + tr("diary.participantsCollapsedHint", "Zum Bearbeiten aufklappen."), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + ) + } + } + Icon( + imageVector = if (participantsSectionExpanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore, + contentDescription = null, + ) + } + } + if (participantsSectionExpanded) { + if (!participantsLoading && participantsError == null && activeMembers.isEmpty()) { + EmptyText(tr("mobile.noMembers", "Keine Mitglieder gefunden")) + } + activeMembers.forEach { member -> + val pRow = participants.find { it.memberId == member.id } + val checked = pRow?.isPresentParticipant() == true + val statusKey = pRow?.attendanceStatus?.lowercase() + val statusLabel = when (statusKey) { + "excused" -> tr("mobile.participantExcused", "Entschuldigt") + "cancelled" -> tr("mobile.participantCancelled", "Abgesagt") + else -> null + } + val toggleThisMember: () -> Unit = t@{ + if (!canWriteDiary || participantMutating || diaryState.isLoading) return@t + val wantAdd = !checked + participantMutating = true + dependencies.applicationScope.launch { + try { + if (wantAdd) { + dependencies.diaryManager.addTrainingParticipant(entry.id, member.id) + } else { + dependencies.diaryManager.removeTrainingParticipant(entry.id, member.id) + } + participants = dependencies.diaryManager.listTrainingParticipants(entry.id) + } finally { + participantMutating = false + } + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + verticalAlignment = Alignment.Top, + ) { + if (canReadMembers && participantsSectionExpanded) { + val mUrl = dependencies.apiConfig.toAbsoluteUrl(memberProfileImagePath(clubId, member.id)) + val auth = dependencies.diaryAuthHeaders() + Box( + modifier = Modifier + .padding(end = 8.dp, top = 4.dp) + .size(40.dp), + ) { + AuthenticatedAsyncImage( + imageUrl = mUrl, + authHeaders = auth, + modifier = Modifier.fillMaxSize(), + contentDescription = member.fullName(), + ) + } + } + Checkbox( + checked = checked, + enabled = canWriteDiary && !participantMutating && !diaryState.isLoading, + onCheckedChange = { wantChecked -> + if (wantChecked == checked) return@Checkbox + toggleThisMember() + }, + ) + Column( + modifier = Modifier + .weight(1f) + .clickable(enabled = canWriteDiary && !participantMutating && !diaryState.isLoading) { toggleThisMember() }, + ) { + Text(member.fullName(), fontWeight = FontWeight.SemiBold) + statusLabel?.let { + Text( + it, + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.primary, + modifier = Modifier.padding(top = 2.dp), + ) + } + if (checked) { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.padding(top = 4.dp), + ) { + TextButton( + enabled = canWriteDiary && !participantMutating && !diaryState.isLoading, + onClick = { + participantMutating = true + dependencies.applicationScope.launch { + try { + dependencies.diaryManager.setTrainingParticipantAttendanceStatus( + entry.id, + member.id, + "excused", + ) + participants = dependencies.diaryManager.listTrainingParticipants(entry.id) + } finally { + participantMutating = false + } + } + }, + ) { Text(tr("mobile.markExcused", "Entschuldigt")) } + TextButton( + enabled = canWriteDiary && !participantMutating && !diaryState.isLoading, + onClick = { + participantMutating = true + dependencies.applicationScope.launch { + try { + dependencies.diaryManager.setTrainingParticipantAttendanceStatus( + entry.id, + member.id, + "cancelled", + ) + participants = dependencies.diaryManager.listTrainingParticipants(entry.id) + } finally { + participantMutating = false + } + } + }, + ) { Text(tr("mobile.markCancelled", "Abgesagt")) } + } + if (planGroups.isNotEmpty()) { + val curG = pRow?.groupId + val curLabel = curG?.let { gid -> + planGroups.find { it.id == gid }?.name ?: "Gruppe $gid" + } ?: tr("diary.participantNoTrainingGroup", "Keine Gruppe") + Box(modifier = Modifier.fillMaxWidth().padding(top = 6.dp)) { + OutlinedButton( + onClick = { participantGroupMenuMemberId = member.id }, + enabled = canWriteDiary && !participantMutating && !diaryState.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("${tr("diary.participantTrainingGroup", "Trainingsgruppe")}: $curLabel") + } + DropdownMenu( + expanded = participantGroupMenuMemberId == member.id, + onDismissRequest = { participantGroupMenuMemberId = null }, + ) { + DropdownMenuItem( + onClick = { + participantGroupMenuMemberId = null + participantMutating = true + dependencies.applicationScope.launch { + try { + dependencies.diaryManager.setTrainingParticipantGroup(entry.id, member.id, null) + participants = dependencies.diaryManager.listTrainingParticipants(entry.id) + } finally { + participantMutating = false + } + } + }, + ) { Text(tr("diary.participantClearGroup", "Keine Gruppe")) } + planGroups.forEach { g -> + DropdownMenuItem( + onClick = { + participantGroupMenuMemberId = null + participantMutating = true + dependencies.applicationScope.launch { + try { + dependencies.diaryManager.setTrainingParticipantGroup(entry.id, member.id, g.id) + participants = dependencies.diaryManager.listTrainingParticipants(entry.id) + } finally { + participantMutating = false + } + } + }, + ) { Text(g.name ?: "Gruppe ${g.id}") } + } + } + } + } + } else if (pRow != null && (statusKey == "excused" || statusKey == "cancelled")) { + TextButton( + enabled = canWriteDiary && !participantMutating && !diaryState.isLoading, + onClick = { + participantMutating = true + dependencies.applicationScope.launch { + try { + dependencies.diaryManager.addTrainingParticipant(entry.id, member.id) + participants = dependencies.diaryManager.listTrainingParticipants(entry.id) + } finally { + participantMutating = false + } + } + }, + modifier = Modifier.padding(top = 4.dp), + ) { Text(tr("mobile.markPresentAgain", "Wieder anwesend")) } + } + TextButton( + enabled = canWriteDiary && !participantMutating && !diaryState.isLoading, + onClick = { + memberNotesSheetMember = member + newMemberContextNote = "" + newMemberContextTagName = "" + memberTagPickMenu = false + }, + modifier = Modifier.padding(top = 4.dp), + ) { Text(tr("diary.memberNotesTags", "Notizen & Tags")) } + } + } + } + } + + SectionTitle(tr("diary.accidents", "Unfälle / Vorfälle")) + Text( + tr("diary.accidentsHint", "Meldungen zu Vorfällen am Trainingstag (wie in der Web-App)."), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(bottom = 6.dp), + ) + accidentSectionError?.let { ErrorText(it) } + accidents.forEach { ev -> + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + elevation = 1.dp, + ) { + Column(modifier = Modifier.padding(12.dp)) { + Text(ev.memberLabel(), fontWeight = FontWeight.SemiBold) + Text(ev.accident, style = MaterialTheme.typography.body2) + } + } + } + Box(modifier = Modifier.fillMaxWidth().padding(top = 4.dp)) { + val amLabel = newAccidentMemberId?.let { mid -> + activeMembers.find { it.id == mid }?.fullName() + } ?: tr("diary.accidentPickMember", "Betroffenes Mitglied") + OutlinedButton( + onClick = { accidentMemberMenu = true }, + enabled = canWriteDiary && !accidentSubmitBusy, + modifier = Modifier.fillMaxWidth(), + ) { Text(amLabel) } + DropdownMenu( + expanded = accidentMemberMenu, + onDismissRequest = { accidentMemberMenu = false }, + ) { + activeMembers.forEach { m -> + DropdownMenuItem( + onClick = { + newAccidentMemberId = m.id + accidentMemberMenu = false + }, + ) { Text(m.fullName()) } + } + } + } + OutlinedTextField( + value = newAccidentDescription, + onValueChange = { newAccidentDescription = it }, + label = { Text(tr("diary.accidentDescription", "Schilderung")) }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + enabled = canWriteDiary && !accidentSubmitBusy, + singleLine = false, + maxLines = 5, + ) + Button( + onClick = { + val mid = newAccidentMemberId ?: return@Button + val desc = newAccidentDescription.trim() + if (desc.isEmpty()) return@Button + dependencies.applicationScope.launch { + accidentSubmitBusy = true + accidentSectionError = null + try { + dependencies.diaryManager.addAccident(clubId, entry.id, mid, desc) + newAccidentDescription = "" + accidents = dependencies.diaryManager.listAccidents(clubId, entry.id) + } catch (t: Throwable) { + accidentSectionError = t.message ?: "Eintrag konnte nicht gespeichert werden" + } finally { + accidentSubmitBusy = false + } + } + }, + enabled = canWriteDiary && !accidentSubmitBusy && newAccidentMemberId != null && newAccidentDescription.isNotBlank(), + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Hinzufügen")) } + + memberNotesSheetMember?.let { sheetMember -> + val notesScroll = rememberScrollState() + AlertDialog( + onDismissRequest = { + memberNotesSheetMember = null + memberNotesError = null + }, + title = { Text("${sheetMember.fullName()} – ${tr("diary.memberNotesTitle", "Notizen & Tags")}") }, + text = { + Column( + modifier = Modifier + .heightIn(max = 480.dp) + .verticalScroll(notesScroll), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + memberNotesError?.let { Text(it, color = MaterialTheme.colors.error) } + if (memberNotesBusy) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + CircularProgressIndicator(modifier = Modifier.size(28.dp)) + } + } + Text(tr("diary.memberNotesSection", "Notizen"), style = MaterialTheme.typography.subtitle2) + memberNotesList.forEach { note -> + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + Text(note.content.orEmpty(), modifier = Modifier.weight(1f)) + TextButton( + enabled = canWriteDiary && !memberNotesBusy, + onClick = { + dependencies.applicationScope.launch { + memberNotesBusy = true + try { + memberNotesList = dependencies.diaryManager.deleteMemberNote( + clubId, + entry.id, + sheetMember.id, + note.id, + ) + } catch (t: Throwable) { + memberNotesError = t.message + } finally { + memberNotesBusy = false + } + } + }, + ) { Text(tr("common.delete", "Löschen")) } + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + OutlinedTextField( + value = newMemberContextNote, + onValueChange = { newMemberContextNote = it }, + label = { Text(tr("mobile.newNote", "Neue Notiz")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !memberNotesBusy, + singleLine = false, + maxLines = 3, + ) + Button( + onClick = { + val text = newMemberContextNote.trim() + if (text.isEmpty()) return@Button + dependencies.applicationScope.launch { + memberNotesBusy = true + try { + memberNotesList = dependencies.diaryManager.addMemberNote( + clubId, + entry.id, + sheetMember.id, + text, + ) + newMemberContextNote = "" + } catch (t: Throwable) { + memberNotesError = t.message + } finally { + memberNotesBusy = false + } + } + }, + enabled = canWriteDiary && !memberNotesBusy && newMemberContextNote.isNotBlank(), + modifier = Modifier.heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Hinzufügen")) } + } + Divider(modifier = Modifier.padding(vertical = 8.dp)) + Text(tr("diary.memberTagsSection", "Tags für diesen Tag"), style = MaterialTheme.typography.subtitle2) + memberTagsList.forEach { link -> + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + Text(link.tagDisplayName(), modifier = Modifier.weight(1f)) + TextButton( + enabled = canWriteDiary && !memberNotesBusy, + onClick = { + val tid = link.tagDefinitionId() + if (tid <= 0) return@TextButton + dependencies.applicationScope.launch { + memberNotesBusy = true + try { + memberTagsList = dependencies.diaryManager.removeMemberTag( + clubId, + entry.id, + sheetMember.id, + tid, + ) + } catch (t: Throwable) { + memberNotesError = t.message + } finally { + memberNotesBusy = false + } + } + }, + ) { Text(tr("mobile.remove", "Entfernen")) } + } + } + Box(modifier = Modifier.fillMaxWidth()) { + OutlinedButton( + onClick = { memberTagPickMenu = true }, + enabled = canWriteDiary && !memberNotesBusy, + modifier = Modifier.fillMaxWidth(), + ) { Text(tr("diary.addExistingMemberTag", "Bestehenden Tag hinzufügen")) } + DropdownMenu( + expanded = memberTagPickMenu, + onDismissRequest = { memberTagPickMenu = false }, + ) { + val linkedIds = memberTagsList.map { it.tagDefinitionId() }.toSet() + val choices = memberNotesAvailableTags.filter { it.id !in linkedIds } + if (choices.isEmpty()) { + DropdownMenuItem(onClick = { memberTagPickMenu = false }) { + Text(tr("diary.noTagsAvailable", "Keine weiteren Tags")) + } + } else { + choices.forEach { tag -> + DropdownMenuItem( + onClick = { + memberTagPickMenu = false + dependencies.applicationScope.launch { + memberNotesBusy = true + try { + memberTagsList = dependencies.diaryManager.addMemberTag( + clubId, + entry.id, + sheetMember.id, + tag.id, + ) + } catch (t: Throwable) { + memberNotesError = t.message + } finally { + memberNotesBusy = false + } + } + }, + ) { Text(tag.name) } + } + } + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + OutlinedTextField( + value = newMemberContextTagName, + onValueChange = { newMemberContextTagName = it }, + label = { Text(tr("diary.newMemberTagName", "Neuer Tag-Name")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !memberNotesBusy, + singleLine = true, + ) + Button( + onClick = { + val name = newMemberContextTagName.trim() + if (name.isEmpty()) return@Button + dependencies.applicationScope.launch { + memberNotesBusy = true + try { + memberTagsList = dependencies.diaryManager.createMemberTagAndLink( + clubId, + entry.id, + sheetMember.id, + name, + ) + newMemberContextTagName = "" + memberNotesAvailableTags = dependencies.diaryManager.listAllDiaryTags() + } catch (t: Throwable) { + memberNotesError = t.message + } finally { + memberNotesBusy = false + } + } + }, + enabled = canWriteDiary && !memberNotesBusy && newMemberContextTagName.isNotBlank(), + modifier = Modifier.heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Hinzufügen")) } + } + } + }, + confirmButton = { + TextButton( + onClick = { + memberNotesSheetMember = null + memberNotesError = null + }, + ) { Text(tr("common.close", "Schließen")) } + }, + ) + } + + OutlinedButton( + onClick = { gallerySectionExpanded = !gallerySectionExpanded }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column(modifier = Modifier.weight(1f)) { + Text(tr("diary.gallerySection", "Gruppenfotos & Galerie"), style = MaterialTheme.typography.subtitle1) + if (!gallerySectionExpanded) { + Text( + tr("diary.galleryCollapsedHint", "Vereins-Galerie aufklappen."), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + ) + } + } + Icon( + imageVector = if (gallerySectionExpanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore, + contentDescription = null, + ) + } + } + if (gallerySectionExpanded && canReadMembers) { + if (canWriteMembers) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth().padding(top = 8.dp), + ) { + OutlinedTextField( + value = newGroupPhotoTitle, + onValueChange = { newGroupPhotoTitle = it }, + label = { Text(tr("diary.groupPhotoTitle", "Titel")) }, + modifier = Modifier.weight(1f), + enabled = !groupPhotoBusy, + singleLine = true, + ) + OutlinedButton( + onClick = { + pickGroupPhoto.launch( + PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly), + ) + }, + enabled = !groupPhotoBusy, + modifier = Modifier.heightIn(min = TouchMinHeight), + ) { Text(tr("diary.uploadGroupPhoto", "Foto wählen")) } + } + OutlinedTextField( + value = newGroupPhotoDescription, + onValueChange = { newGroupPhotoDescription = it }, + label = { Text(tr("diary.groupPhotoDescription", "Beschreibung (optional)")) }, + modifier = Modifier.fillMaxWidth(), + enabled = !groupPhotoBusy, + singleLine = false, + maxLines = 3, + ) + } + if (groupPhotoBusy) LoadingInline() + if (groupPhotos.isEmpty()) { + Text(tr("diary.noGroupPhotos", "Keine Gruppenfotos."), modifier = Modifier.padding(vertical = 8.dp)) + } else { + val auth = dependencies.diaryAuthHeaders() + groupPhotos.forEach { ph -> + val url = ph.imageUrl?.let { dependencies.apiConfig.toAbsoluteUrl(it) } + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp), + elevation = 1.dp, + ) { + Column(modifier = Modifier.padding(8.dp)) { + Text(ph.title.orEmpty(), fontWeight = FontWeight.SemiBold) + if (!ph.description.isNullOrBlank()) { + Text(ph.description.orEmpty(), style = MaterialTheme.typography.caption) + } + if (url != null) { + AuthenticatedAsyncImage( + imageUrl = url, + authHeaders = auth, + modifier = Modifier + .fillMaxWidth() + .height(180.dp) + .padding(top = 6.dp), + contentDescription = ph.title, + ) + } + if (canWriteMembers) { + TextButton( + enabled = !groupPhotoBusy, + onClick = { + dependencies.applicationScope.launch { + groupPhotoBusy = true + try { + dependencies.diaryManager.deleteMemberGroupPhoto(clubId, ph.id) + groupPhotos = dependencies.diaryManager.listMemberGroupPhotos(clubId) + } finally { + groupPhotoBusy = false + } + } + }, + ) { Text(tr("common.delete", "Löschen")) } + } + } + } + } + } + } + + SectionTitle(tr("diary.dayActivitiesSection", "Weitere Tages-Aktivitäten")) + Text( + tr("diary.dayActivitiesHint", "Kurze Einträge zum Trainingstag (API /activities – unabhängig vom Trainingsplan)."), + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(bottom = 8.dp), + ) + freeformActivities.forEach { fa -> + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + elevation = 1.dp, + ) { + Text(fa.description, modifier = Modifier.padding(12.dp), style = MaterialTheme.typography.body2) + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + OutlinedTextField( + value = newFreeformDescription, + onValueChange = { newFreeformDescription = it }, + label = { Text(tr("diary.newDayActivity", "Neuer Eintrag")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !freeformBusy && !planLoading, + singleLine = false, + maxLines = 3, + ) + Button( + onClick = { + val text = newFreeformDescription.trim() + if (text.isEmpty()) return@Button + dependencies.applicationScope.launch { + freeformBusy = true + try { + dependencies.diaryManager.addFreeformActivity(entry.id, text) + newFreeformDescription = "" + freeformActivities = dependencies.diaryManager.listFreeformActivities(entry.id) + } finally { + freeformBusy = false + } + } + }, + enabled = canWriteDiary && !freeformBusy && newFreeformDescription.isNotBlank(), + modifier = Modifier.heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Hinzufügen")) } + } + + SectionTitle(tr("diary.trainingPlan", "Trainingsplan")) + ErrorText(planActionError) + if (canReadDiary) { + val clubName = clubState.clubs.firstOrNull { it.id == clubId }?.name ?: "Verein" + val dateStr = formatDate(entry.date) + val timeStr = formatTimeRange(entry.trainingStart, entry.trainingEnd) + val tb = tr("diary.timeblock", "Zeitblock") + val sharePlanTitle = tr("diary.sharePdfPlan", "Trainingsplan teilen") + val shareDayTitle = tr("diary.sharePdfDay", "Tages-PDF teilen") + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp), + ) { + OutlinedButton( + onClick = { + runCatching { + val f = File(androidContext.cacheDir, "trainingsplan-${entry.id}.pdf") + writeTrainingPlanPdf(f, clubName, dateStr, timeStr, planItems, tb) + sharePdfFile(androidContext, f, sharePlanTitle) + } + }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(tr("diary.trainingPlanPdfShort", "Plan-PDF")) } + OutlinedButton( + onClick = { + runCatching { + val f = File(androidContext.cacheDir, "trainingstag-${entry.id}.pdf") + writeTrainingDaySummaryPdf( + f, + clubName, + dateStr, + timeStr, + activeMembers, + participants, + freeformActivities, + planItems, + tb, + ) + sharePdfFile(androidContext, f, shareDayTitle) + } + }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(tr("diary.trainingDaySummaryPdfShort", "Tag-PDF")) } + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + OutlinedButton( + onClick = refreshPlanAfterMutation, + enabled = !planMutating && !planLoading, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.refresh", "Aktualisieren")) } + OutlinedButton( + onClick = { showAddPlanActivity = !showAddPlanActivity }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(tr("diary.addPlanActivity", "Neue Aktivität")) } + } + if (planGroups.isNotEmpty()) { + OutlinedButton( + onClick = { showAddPlanGroupActivity = !showAddPlanGroupActivity }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("diary.addGroupScopedActivity", "Aktivität in Trainingsgruppe")) } + } + OutlinedButton( + onClick = { showAddTrainingGroup = !showAddTrainingGroup }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("diary.addTrainingGroup", "Neue Trainingsgruppe")) } + if (planGroups.isNotEmpty()) { + Text( + tr("diary.trainingGroupsForDay", "Trainingsgruppen dieses Tages"), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(top = 12.dp, bottom = 4.dp), + ) + planGroups.forEach { g -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + g.name ?: "Gruppe ${g.id}", + style = MaterialTheme.typography.body2, + modifier = Modifier.weight(1f), + ) + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.deleteTrainingGroup(g.id, clubId, entry.id) + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + ) { Text(tr("common.delete", "Löschen")) } + } + } + } + if (showAddPlanActivity) { + var newActGroupMenu by remember { mutableStateOf(false) } + Text( + tr("diary.addPlanActivityHint", "Name wie in der Web-App (wird ggf. als neue Übung angelegt). Optional: Dauer und Zuordnung zu einer Trainingsgruppe."), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(top = 12.dp, bottom = 8.dp), + ) + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = newPlanIsTimeblock, + onCheckedChange = { newPlanIsTimeblock = it }, + enabled = canWriteDiary && !planMutating, + ) + Text(tr("diary.planTimeblock", "Zeitblock"), modifier = Modifier.clickable(enabled = canWriteDiary && !planMutating) { newPlanIsTimeblock = !newPlanIsTimeblock }) + } + if (!newPlanIsTimeblock) { + Text( + tr( + "diary.predefinedSearchHint", + "Vordefinierte Übung: nach Kürzel suchen (mind. 2 Zeichen) oder Standardliste öffnen.", + ), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(top = 8.dp, bottom = 4.dp), + ) + OutlinedTextField( + value = newPlanPredefQuery, + onValueChange = { newPlanPredefQuery = it }, + label = { Text(tr("diary.predefinedSearchLabel", "Kürzel-Suche")) }, + modifier = Modifier.fillMaxWidth(), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + if (newPlanPredefLoading) { + Text(tr("mobile.searching", "Suche …"), style = MaterialTheme.typography.caption) + } + newPlanPredefResults.take(12).forEach { dto -> + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + newPlanPickedPredefined = dto + newPlanActivityName = dto.predefinedDtoDisplayLabel() + dto.duration?.let { newPlanDuration = it.toString() } + newPlanDurationText = dto.durationText.orEmpty() + }, + modifier = Modifier.fillMaxWidth(), + ) { + val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty() + Text(dto.predefinedDtoDisplayLabel() + code) + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { newPlanStandardPredefExpanded = !newPlanStandardPredefExpanded }, + ) { + Text( + if (newPlanStandardPredefExpanded) { + tr("diary.predefinedHideStandard", "Standardliste ausblenden") + } else { + tr("diary.predefinedShowStandard", "Standard-Übungen") + }, + ) + } + TextButton( + enabled = canWriteDiary && !planMutating && newPlanPickedPredefined != null, + onClick = { newPlanPickedPredefined = null }, + ) { Text(tr("diary.predefinedClearPick", "Auswahl zurücksetzen")) } + } + if (newPlanStandardPredefExpanded) { + newPlanStandardPredefList.take(24).forEach { dto -> + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + newPlanPickedPredefined = dto + newPlanActivityName = dto.predefinedDtoDisplayLabel() + dto.duration?.let { newPlanDuration = it.toString() } + newPlanDurationText = dto.durationText.orEmpty() + }, + modifier = Modifier.fillMaxWidth(), + ) { + val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty() + Text(dto.predefinedDtoDisplayLabel() + code) + } + } + } + newPlanPickedPredefined?.let { picked -> + Text( + tr("diary.predefinedPicked", "Ausgewählt") + ": ${picked.predefinedDtoDisplayLabel()}", + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.primary, + ) + } + } + OutlinedTextField( + value = newPlanActivityName, + onValueChange = { + newPlanActivityName = it + newPlanPickedPredefined = null + }, + label = { Text(tr("diary.planActivityName", "Aktivität / Bezeichnung")) }, + modifier = Modifier.fillMaxWidth(), + enabled = canWriteDiary && !planMutating && !newPlanIsTimeblock, + singleLine = true, + ) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = newPlanDuration, + onValueChange = { newPlanDuration = it }, + label = { Text(tr("diary.planDurationMin", "Minuten")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + OutlinedTextField( + value = newPlanDurationText, + onValueChange = { newPlanDurationText = it }, + label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + } + if (planGroups.isNotEmpty()) { + Box(modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) { + val assignLabel = newPlanTargetGroupId?.let { id -> + planGroups.find { it.id == id }?.name ?: "Gruppe $id" + } ?: tr("diary.planGroupGlobal", "Alle / keine Gruppe") + OutlinedButton( + onClick = { newActGroupMenu = true }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier.fillMaxWidth(), + ) { Text("${tr("diary.planAssignGroup", "Zuordnung")}: $assignLabel") } + DropdownMenu(expanded = newActGroupMenu, onDismissRequest = { newActGroupMenu = false }) { + DropdownMenuItem(onClick = { + newPlanTargetGroupId = null + newActGroupMenu = false + }) { Text(tr("diary.planGroupGlobal", "Alle / keine Gruppe")) } + planGroups.forEach { g -> + DropdownMenuItem(onClick = { + newPlanTargetGroupId = g.id + newActGroupMenu = false + }) { Text(g.name ?: "Gruppe ${g.id}") } + } + } + } + } + Button( + onClick = { + if (!newPlanIsTimeblock && newPlanActivityName.isBlank()) return@Button + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.createPlanActivity( + clubId, + CreateDiaryPlanActivityRequest( + diaryDateId = entry.id, + activity = if (newPlanIsTimeblock) "" else newPlanActivityName.trim(), + predefinedActivityId = newPlanPickedPredefined?.id, + duration = newPlanDuration.toIntOrNull(), + durationText = newPlanDurationText.trim().takeIf { it.isNotEmpty() }, + isTimeblock = newPlanIsTimeblock, + groupId = newPlanTargetGroupId, + ), + ) + newPlanActivityName = "" + newPlanDuration = "" + newPlanDurationText = "" + newPlanIsTimeblock = false + newPlanTargetGroupId = null + newPlanPredefQuery = "" + newPlanPredefResults = emptyList() + newPlanPickedPredefined = null + showAddPlanActivity = false + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Hinzufügen")) } + } + if (showAddPlanGroupActivity) { + var scopedGroupMenu by remember { mutableStateOf(false) } + Text( + tr("diary.addGroupActivityHint", "Legt eine Aktivität in einer bestehenden Trainingsgruppe an (wie Web „Gruppen-Aktivität“)."), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(top = 12.dp, bottom = 8.dp), + ) + if (planGroups.isEmpty()) { + Text(tr("diary.needTrainingGroupFirst", "Lege zuerst eine Trainingsgruppe an."), modifier = Modifier.padding(vertical = 8.dp)) + } else { + Box(modifier = Modifier.fillMaxWidth()) { + val gl = newPlanGroupActivityGroupId?.let { id -> + planGroups.find { it.id == id }?.name ?: "Gruppe $id" + } ?: tr("diary.pickTrainingGroup", "Trainingsgruppe wählen") + OutlinedButton( + onClick = { scopedGroupMenu = true }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier.fillMaxWidth(), + ) { Text(gl) } + DropdownMenu(expanded = scopedGroupMenu, onDismissRequest = { scopedGroupMenu = false }) { + planGroups.forEach { g -> + DropdownMenuItem(onClick = { + newPlanGroupActivityGroupId = g.id + scopedGroupMenu = false + }) { Text(g.name ?: "Gruppe ${g.id}") } + } + } + } + Text( + tr( + "diary.predefinedSearchHint", + "Vordefinierte Übung: nach Kürzel suchen (mind. 2 Zeichen) oder Standardliste öffnen.", + ), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(top = 8.dp, bottom = 4.dp), + ) + OutlinedTextField( + value = scopedPredefQuery, + onValueChange = { scopedPredefQuery = it }, + label = { Text(tr("diary.predefinedSearchLabel", "Kürzel-Suche")) }, + modifier = Modifier.fillMaxWidth(), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + if (scopedPredefLoading) { + Text(tr("mobile.searching", "Suche …"), style = MaterialTheme.typography.caption) + } + scopedPredefResults.take(12).forEach { dto -> + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + scopedPickedPredefined = dto + newScopedActivityName = dto.predefinedDtoDisplayLabel() + dto.duration?.let { newScopedDuration = it.toString() } + newScopedDurationText = dto.durationText.orEmpty() + }, + modifier = Modifier.fillMaxWidth(), + ) { + val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty() + Text(dto.predefinedDtoDisplayLabel() + code) + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), + ) { + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { scopedStandardPredefExpanded = !scopedStandardPredefExpanded }, + ) { + Text( + if (scopedStandardPredefExpanded) { + tr("diary.predefinedHideStandard", "Standardliste ausblenden") + } else { + tr("diary.predefinedShowStandard", "Standard-Übungen") + }, + ) + } + TextButton( + enabled = canWriteDiary && !planMutating && scopedPickedPredefined != null, + onClick = { scopedPickedPredefined = null }, + ) { Text(tr("diary.predefinedClearPick", "Auswahl zurücksetzen")) } + } + if (scopedStandardPredefExpanded) { + scopedStandardPredefList.take(24).forEach { dto -> + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + scopedPickedPredefined = dto + newScopedActivityName = dto.predefinedDtoDisplayLabel() + dto.duration?.let { newScopedDuration = it.toString() } + newScopedDurationText = dto.durationText.orEmpty() + }, + modifier = Modifier.fillMaxWidth(), + ) { + val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty() + Text(dto.predefinedDtoDisplayLabel() + code) + } + } + } + scopedPickedPredefined?.let { picked -> + Text( + tr("diary.predefinedPicked", "Ausgewählt") + ": ${picked.predefinedDtoDisplayLabel()}", + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.primary, + ) + } + OutlinedTextField( + value = newScopedActivityName, + onValueChange = { + newScopedActivityName = it + scopedPickedPredefined = null + }, + label = { Text(tr("diary.planActivityName", "Aktivität / Bezeichnung")) }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = newScopedDuration, + onValueChange = { newScopedDuration = it }, + label = { Text(tr("diary.planDurationMin", "Minuten")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + OutlinedTextField( + value = newScopedDurationText, + onValueChange = { newScopedDurationText = it }, + label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + } + Button( + onClick = { + val gid = newPlanGroupActivityGroupId + if (gid == null || newScopedActivityName.isBlank()) return@Button + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.addPlanGroupActivity( + AddDiaryPlanGroupActivityRequest( + clubId = clubId, + diaryDateId = entry.id, + groupId = gid, + activity = newScopedActivityName.trim(), + predefinedActivityId = scopedPickedPredefined?.id, + timeblockId = null, + duration = newScopedDuration.toIntOrNull(), + durationText = newScopedDurationText.trim().takeIf { it.isNotEmpty() }, + ), + ) + newScopedActivityName = "" + newScopedDuration = "" + newScopedDurationText = "" + scopedPredefQuery = "" + scopedPredefResults = emptyList() + scopedPickedPredefined = null + newPlanGroupActivityGroupId = null + showAddPlanGroupActivity = false + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + enabled = canWriteDiary && !planMutating && planGroups.isNotEmpty(), + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Hinzufügen")) } + } + } + if (showAddTrainingGroup) { + OutlinedTextField( + value = newTrainingGroupName, + onValueChange = { newTrainingGroupName = it }, + label = { Text(tr("diary.trainingGroupName", "Name der Gruppe")) }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + OutlinedTextField( + value = newTrainingGroupLead, + onValueChange = { newTrainingGroupLead = it }, + label = { Text(tr("diary.trainingGroupLead", "Übungsleitung (optional)")) }, + modifier = Modifier.fillMaxWidth(), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + Button( + onClick = { + val n = newTrainingGroupName.trim() + if (n.isEmpty()) return@Button + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.createTrainingGroup( + clubId, + entry.id, + n, + newTrainingGroupLead.trim().takeIf { it.isNotEmpty() }, + ) + newTrainingGroupName = "" + newTrainingGroupLead = "" + showAddTrainingGroup = false + planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + .heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.add", "Anlegen")) } + } + if (!planLoading && planError == null && planItems.isEmpty()) { + EmptyText(tr("diary.planEmpty", "Keine geplanten Aktivitäten")) + } + val sortedPlan = remember(planItems) { + planItems.sortedWith(::diaryPlanItemComparator) + } + sortedPlan.forEach { item -> + val cfg = dependencies.apiConfig + val mainImg = item.mainActivityImagePath()?.let { cfg.toAbsoluteUrl(it) } + val nestedImg = item.groupActivities.firstNotNullOfOrNull { ga -> + ga.nestedActivityImagePath()?.let { cfg.toAbsoluteUrl(it) } + } + DiaryPlanEditableCard( + item = item, + allPlanItems = planItems, + planGroups = planGroups, + planMutating = planMutating, + canWriteDiary = canWriteDiary, + mainImageUrl = mainImg, + nestedImageUrl = nestedImg, + canReadImages = canReadDiary, + onOpenImage = { url -> planImageViewerUrl = url }, + onEdit = { editingPlanItem = item }, + onDelete = { + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.deletePlanActivity(clubId, item.id) + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + onMoveUp = { + val scope = sameTrainingPlanScope(planItems, item) + val idx = scope.indexOfFirst { it.id == item.id } + if (idx > 0) { + val targetOrder = scope[idx - 1].orderId + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.updatePlanActivityOrder(clubId, item.id, targetOrder) + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + } + }, + onMoveDown = { + val scope = sameTrainingPlanScope(planItems, item) + val idx = scope.indexOfFirst { it.id == item.id } + if (idx >= 0 && idx < scope.lastIndex) { + val targetOrder = scope[idx + 1].orderId + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.updatePlanActivityOrder(clubId, item.id, targetOrder) + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + } + }, + onDeleteNested = { nestedId -> + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + dependencies.diaryManager.deletePlanNestedGroupActivity(clubId, nestedId) + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + ) + val timeblockLabelForAssign = tr("diary.timeblock", "Zeitblock") + val assignPairs: List> = if (item.groupActivities.isNotEmpty()) { + item.groupActivities.mapNotNull { ga -> + val gid = ga.id ?: return@mapNotNull null + val title = ga.groupPredefinedActivity.displayLabel().ifBlank { + "${tr("mobile.activityFallback", "Aktivität")} $gid" + } + gid to title + } + } else { + listOf( + item.id to item.displayTitle(timeblockLabelForAssign).ifBlank { + "${tr("mobile.activityFallback", "Aktivität")} ${item.id}" + }, + ) + } + assignPairs.forEach { (activityApiId, title) -> + PlanRowMemberAssignExpandable( + clubId = clubId, + diaryDateId = entry.id, + activityApiId = activityApiId, + subtitle = title, + dependencies = dependencies, + canWriteDiary = canWriteDiary, + presentMembers = presentMembersForPlan, + participantRows = participants, + expandedId = expandedMemberAssignActivityId, + onExpandedIdChange = { expandedMemberAssignActivityId = it }, + memberAssignMap = memberAssignMap, + onMemberAssignMapChange = { memberAssignMap = it }, + busy = memberAssignBusy, + onBusyChange = { memberAssignBusy = it }, + onParticipantsReplace = { participants = it }, + ) + } + } + + editingPlanItem?.let { editItem -> + var editGroupMenu by remember { mutableStateOf(false) } + AlertDialog( + onDismissRequest = { if (!planMutating) editingPlanItem = null }, + title = { Text(tr("diary.editPlanItem", "Eintrag bearbeiten")) }, + text = { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + if (!editItem.isTimeblock) { + Text( + tr( + "diary.predefinedSearchHint", + "Vordefinierte Übung: nach Kürzel suchen (mind. 2 Zeichen) oder Standardliste öffnen.", + ), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + ) + OutlinedTextField( + value = editPlanPredefQuery, + onValueChange = { editPlanPredefQuery = it }, + label = { Text(tr("diary.predefinedSearchLabel", "Kürzel-Suche")) }, + modifier = Modifier.fillMaxWidth(), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + if (editPlanPredefLoading) { + Text(tr("mobile.searching", "Suche …"), style = MaterialTheme.typography.caption) + } + editPlanPredefResults.take(10).forEach { dto -> + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + editPlanPickedPredefinedId = dto.id + editPlanName = dto.predefinedDtoDisplayLabel() + dto.duration?.let { editPlanDuration = it.toString() } + editPlanDurationText = dto.durationText.orEmpty() + }, + modifier = Modifier.fillMaxWidth(), + ) { + val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty() + Text(dto.predefinedDtoDisplayLabel() + code) + } + } + TextButton( + enabled = canWriteDiary && !planMutating && editPlanPickedPredefinedId != null, + onClick = { editPlanPickedPredefinedId = null }, + ) { Text(tr("diary.predefinedClearPick", "Auswahl zurücksetzen")) } + OutlinedTextField( + value = editPlanName, + onValueChange = { + editPlanName = it + editPlanPickedPredefinedId = null + }, + label = { Text(tr("diary.planActivityName", "Aktivität / Bezeichnung")) }, + modifier = Modifier.fillMaxWidth(), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + } else { + Text(tr("diary.planTimeblockEditHint", "Zeitblock – nur Dauer anpassbar."), style = MaterialTheme.typography.caption) + } + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = editPlanDuration, + onValueChange = { editPlanDuration = it }, + label = { Text(tr("diary.planDurationMin", "Minuten")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + OutlinedTextField( + value = editPlanDurationText, + onValueChange = { editPlanDurationText = it }, + label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) }, + modifier = Modifier.weight(1f), + enabled = canWriteDiary && !planMutating, + singleLine = true, + ) + } + if (planGroups.isNotEmpty()) { + Box(modifier = Modifier.fillMaxWidth()) { + val el = editPlanGroupId?.let { id -> + planGroups.find { it.id == id }?.name ?: "Gruppe $id" + } ?: tr("diary.planGroupGlobal", "Alle / keine Gruppe") + OutlinedButton( + onClick = { editGroupMenu = true }, + enabled = canWriteDiary && !planMutating, + modifier = Modifier.fillMaxWidth(), + ) { Text("${tr("diary.planAssignGroup", "Zuordnung")}: $el") } + DropdownMenu(expanded = editGroupMenu, onDismissRequest = { editGroupMenu = false }) { + DropdownMenuItem(onClick = { + editPlanGroupId = null + editGroupMenu = false + }) { Text(tr("diary.planGroupGlobal", "Alle / keine Gruppe")) } + planGroups.forEach { g -> + DropdownMenuItem(onClick = { + editPlanGroupId = g.id + editGroupMenu = false + }) { Text(g.name ?: "Gruppe ${g.id}") } + } + } + } + } + } + }, + confirmButton = { + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { + dependencies.applicationScope.launch { + planMutating = true + planActionError = null + try { + val pickedPredef = editPlanPickedPredefinedId + val updateBody = when { + editItem.isTimeblock -> UpdateDiaryPlanActivityRequest( + duration = editPlanDuration.toIntOrNull(), + durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() }, + groupId = editPlanGroupId, + ) + pickedPredef != null -> UpdateDiaryPlanActivityRequest( + predefinedActivityId = pickedPredef, + duration = editPlanDuration.toIntOrNull(), + durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() }, + groupId = editPlanGroupId, + ) + editPlanName.isNotBlank() -> UpdateDiaryPlanActivityRequest( + customActivityName = editPlanName.trim(), + duration = editPlanDuration.toIntOrNull(), + durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() }, + groupId = editPlanGroupId, + ) + else -> UpdateDiaryPlanActivityRequest( + duration = editPlanDuration.toIntOrNull(), + durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() }, + groupId = editPlanGroupId, + ) + } + dependencies.diaryManager.updatePlanActivity( + clubId, + editItem.id, + updateBody, + ) + editingPlanItem = null + planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id) + planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id) + } catch (t: Throwable) { + planActionError = t.message + } finally { + planMutating = false + } + } + }, + ) { Text(tr("common.save", "Speichern")) } + }, + dismissButton = { + TextButton( + enabled = canWriteDiary && !planMutating, + onClick = { editingPlanItem = null }, + ) { Text(tr("mobile.cancel", "Abbrechen")) } + }, + ) + } + + SectionTitle("Tags") + FlowRowText(items = entry.diaryTags.map { it.name }) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = tagName, + onValueChange = { tagName = it }, + label = { Text(tr("mobile.newTag", "Neuer Tag")) }, + modifier = Modifier.weight(1f), + singleLine = true, + ) + Button(onClick = { + val value = tagName.trim() + if (value.isNotEmpty()) { + dependencies.applicationScope.launch { + dependencies.diaryManager.createAndLinkTag(clubId, entry.id, value) + tagName = "" + } + } + }, enabled = canWriteDiary) { Text(tr("mobile.add", "Add")) } + } + entry.diaryTags.forEach { tag -> + TextButton(onClick = { dependencies.applicationScope.launch { dependencies.diaryManager.removeTag(clubId, tag.id) } }, enabled = canWriteDiary) { + Text("${tr("mobile.deleteTag", "Tag entfernen")}: ${tag.name}") + } + } + + SectionTitle("Notizen") + entry.diaryNotes.forEach { noteEntry -> + Card(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), elevation = 1.dp) { + Row(modifier = Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) { + Text(noteEntry.content.orEmpty(), modifier = Modifier.weight(1f)) + TextButton(onClick = { dependencies.applicationScope.launch { dependencies.diaryManager.deleteNote(clubId, noteEntry.id) } }, enabled = canWriteDiary) { + Text(tr("common.delete", "Löschen")) + } + } + } + } + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = note, + onValueChange = { note = it }, + label = { Text(tr("mobile.newNote", "Neue Notiz")) }, + modifier = Modifier.weight(1f), + ) + Button(onClick = { + val value = note.trim() + if (value.isNotEmpty()) { + dependencies.applicationScope.launch { + dependencies.diaryManager.addNote(clubId, entry.id, value) + note = "" + } + } + }, enabled = canWriteDiary) { Text(tr("mobile.add", "Add")) } + } + planImageViewerUrl?.let { u -> + val auth = dependencies.diaryAuthHeaders() + AlertDialog( + onDismissRequest = { planImageViewerUrl = null }, + confirmButton = { + TextButton(onClick = { planImageViewerUrl = null }) { + Text(tr("common.close", "Schließen")) + } + }, + title = { Text(tr("diary.planImageTitle", "Übungsbild")) }, + text = { + Box(Modifier.fillMaxWidth().heightIn(min = 200.dp)) { + AuthenticatedAsyncImage( + imageUrl = u, + authHeaders = auth, + modifier = Modifier.fillMaxWidth(), + contentDescription = null, + ) + } + }, + ) + } + } + } +} + +private sealed class MembersStackRoute { + object Browse : MembersStackRoute() + data class Detail(val memberId: Int) : MembersStackRoute() + /** `null` = neues Mitglied anlegen */ + data class Edit(val memberId: Int?) : MembersStackRoute() +} + +private fun trainingWeekdayLabel(weekday: Int): String = when (weekday) { + 0 -> "So" + 1 -> "Mo" + 2 -> "Di" + 3 -> "Mi" + 4 -> "Do" + 5 -> "Fr" + 6 -> "Sa" + else -> "?$weekday" +} + +private fun displayActivityDate(raw: String?): String { + if (raw.isNullOrBlank()) return "-" + val s = raw.trim() + return s.take(10).ifBlank { s } +} + +@Composable +private fun MembersScreen( + dependencies: AppDependencies, + onNestedOpenChange: (Boolean) -> Unit, +) { + val clubState by dependencies.clubManager.state.collectAsState() + val membersState by dependencies.membersManager.state.collectAsState() + val clubId = clubState.currentClubId ?: return + val canReadMembers = clubState.currentPermissions?.canReadMembers() == true + val canWriteMembers = clubState.currentPermissions?.canWriteMembers() == true + var stack by remember { mutableStateOf(MembersStackRoute.Browse) } + var query by rememberSaveable { mutableStateOf("") } + + LaunchedEffect(stack, clubId) { + onNestedOpenChange(stack !is MembersStackRoute.Browse) + } + + DisposableEffect(Unit) { + onDispose { onNestedOpenChange(false) } + } + + LaunchedEffect(clubId) { + stack = MembersStackRoute.Browse + dependencies.membersManager.loadMembers(clubId) + } + + if (!canReadMembers) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp), + ) { + Header(tr("members.title", "Mitglieder")) + Text(tr("members.noReadPermission", "Keine Berechtigung zur Mitgliederliste.")) + } + return + } + + when (val route = stack) { + is MembersStackRoute.Detail -> { + val member = membersState.members.firstOrNull { it.id == route.memberId } + if (member == null) { + LaunchedEffect(route.memberId, membersState.members) { + if (!membersState.isLoading && membersState.members.isNotEmpty()) { + stack = MembersStackRoute.Browse + } + } + Column(Modifier.fillMaxSize().padding(ScreenHorizontalPadding)) { + if (membersState.isLoading) LoadingInline() + else Text(tr("members.memberNotFound", "Mitglied nicht gefunden."), modifier = Modifier.padding(16.dp)) + TextButton(onClick = { stack = MembersStackRoute.Browse }) { Text(tr("mobile.back", "Zurück")) } + } + } else { + MemberDetailRoute( + clubId = clubId, + member = member, + dependencies = dependencies, + canWriteMembers = canWriteMembers, + onBack = { stack = MembersStackRoute.Browse }, + onEdit = { stack = MembersStackRoute.Edit(member.id) }, + ) + } + } + is MembersStackRoute.Edit -> { + val seed = route.memberId?.let { id -> membersState.members.firstOrNull { it.id == id } } + MemberEditRoute( + clubId = clubId, + seedMember = seed, + dependencies = dependencies, + canWriteMembers = canWriteMembers, + onBack = { + stack = if (route.memberId != null) MembersStackRoute.Detail(route.memberId) else MembersStackRoute.Browse + }, + onSaved = { newId -> + dependencies.applicationScope.launch { dependencies.membersManager.loadMembers(clubId) } + stack = if (newId != null) MembersStackRoute.Detail(newId) else MembersStackRoute.Browse + }, + ) + } + MembersStackRoute.Browse -> { + val filteredMembers = remember(membersState.members, query) { + val normalizedQuery = query.trim().lowercase() + if (normalizedQuery.isEmpty()) { + membersState.members + } else { + membersState.members.filter { member -> + listOf( + member.fullName(), + member.firstName, + member.lastName, + member.birthDate.orEmpty(), + member.gender.orEmpty(), + member.city.orEmpty(), + member.street.orEmpty(), + ).any { value -> value.lowercase().contains(normalizedQuery) } + } + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp), + ) { + Header(tr("members.title", "Mitglieder")) + Text( + tr("members.listHint", "Suche nach Namen oder tippe auf ein Mitglied für die Details."), + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(bottom = 10.dp), + ) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + OutlinedButton( + onClick = { dependencies.applicationScope.launch { dependencies.membersManager.loadMembers(clubId) } }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(tr("mobile.refresh", "Aktualisieren")) } + if (canWriteMembers) { + Button( + onClick = { stack = MembersStackRoute.Edit(null) }, + modifier = Modifier + .weight(1f) + .heightIn(min = TouchMinHeight), + ) { Text(tr("members.newMember", "Neu")) } + } + } + OutlinedTextField( + value = query, + onValueChange = { query = it }, + label = { Text(tr("mobile.search", "Suche")) }, + modifier = Modifier.fillMaxWidth().padding(vertical = 10.dp), + singleLine = true, + ) + if (membersState.isLoading) LoadingInline() + ErrorText(membersState.error) + if (!membersState.isLoading && filteredMembers.isEmpty()) { + EmptyText(if (query.isBlank()) tr("mobile.noMembers", "Keine Mitglieder gefunden") else tr("mobile.noResults", "Keine Treffer")) + } + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + items(filteredMembers) { member -> + Card( + modifier = Modifier.fillMaxWidth().clickable { stack = MembersStackRoute.Detail(member.id) }, + elevation = 1.dp, + ) { + Row(modifier = Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically) { + val avatarUrl = memberProfileImagePath(clubId, member.id) + ?.let { dependencies.apiConfig.toAbsoluteUrl(it) } + if (canReadMembers && avatarUrl != null) { + AuthenticatedAsyncImage( + imageUrl = avatarUrl, + authHeaders = dependencies.diaryAuthHeaders(), + modifier = Modifier.size(44.dp), + contentDescription = member.fullName(), + ) + Spacer(modifier = Modifier.width(12.dp)) + } + Column(modifier = Modifier.weight(1f)) { + Text(member.fullName(), fontWeight = FontWeight.SemiBold) + Text( + listOfNotNull( + member.ttr?.let { "TTR $it" }, + member.qttr?.let { "QTTR $it" }, + ).joinToString(" · ").ifBlank { member.city ?: "" }, + style = MaterialTheme.typography.caption, + ) + } + } + } + } + } + } + } + } +} + +@Composable +private fun MemberDetailRoute( + clubId: Int, + member: Member, + dependencies: AppDependencies, + canWriteMembers: Boolean, + onBack: () -> Unit, + onEdit: () -> Unit, +) { + BackHandler(onBack = onBack) + val scroll = rememberScrollState() + val narrowPhone = LocalConfiguration.current.screenWidthDp < MAIN_NAV_RAIL_MIN_WIDTH_DP + val androidContext = LocalContext.current + var activityPeriod by rememberSaveable { mutableStateOf("year") } + var activityStats by remember { mutableStateOf>(emptyList()) } + var lastParts by remember { mutableStateOf>(emptyList()) } + var memberGroups by remember { mutableStateOf>(emptyList()) } + var allGroups by remember { mutableStateOf>(emptyList()) } + var schedule by remember { mutableStateOf>(emptyList()) } + var detailBusy by remember { mutableStateOf(false) } + var detailError by remember { mutableStateOf(null) } + var groupMenu by remember { mutableStateOf(false) } + + LaunchedEffect(member.id, clubId, activityPeriod) { + detailBusy = true + detailError = null + try { + coroutineScope { + val a = async { runCatching { dependencies.membersManager.memberActivityStats(clubId, member.id, activityPeriod) }.getOrDefault(emptyList()) } + val b = async { runCatching { dependencies.membersManager.memberLastParticipations(clubId, member.id) }.getOrDefault(emptyList()) } + val c = async { runCatching { dependencies.membersManager.listMemberTrainingGroups(clubId, member.id) }.getOrDefault(emptyList()) } + val d = async { runCatching { dependencies.membersManager.listTrainingGroups(clubId) }.getOrDefault(emptyList()) } + val e = async { runCatching { dependencies.membersManager.trainingScheduleGroups(clubId) }.getOrDefault(emptyList()) } + activityStats = a.await() + lastParts = b.await() + memberGroups = c.await() + allGroups = d.await() + schedule = e.await() + } + } catch (t: Throwable) { + detailError = t.message + } finally { + detailBusy = false + } + } + + val cropPortrait = rememberMemberPortraitCropLauncher( + onCropped = { bytes -> + dependencies.applicationScope.launch { + runCatching { + dependencies.membersManager.uploadMemberPortrait(clubId, member.id, bytes) + dependencies.membersManager.loadMembers(clubId) + } + } + }, + ) + + val pickPortrait = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> + if (uri == null || !canWriteMembers) return@rememberLauncherForActivityResult + runCatching { + val sourceForCrop = copyPickedImageToCacheForCrop(androidContext, uri, member.id) + val (_, destUri) = createCropOutputUri(androidContext, member.id) + cropPortrait.launch(buildMemberPortraitCropIntent(androidContext, sourceForCrop, destUri)) + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 12.dp) + .verticalScroll(scroll), + ) { + ScreenTopBar( + title = member.fullName(), + onBack = onBack, + hint = if (narrowPhone) { + tr("mobile.detailNavHint", "Zurück: Pfeil oben links oder die System-Taste „Zurück“.") + } else { + null + }, + ) + Spacer(modifier = Modifier.height(8.dp)) + if (canWriteMembers) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) { + Button(onClick = onEdit, modifier = Modifier.weight(1f).heightIn(min = TouchMinHeight)) { + Text(tr("members.editMember", "Bearbeiten")) + } + OutlinedButton( + onClick = { + pickPortrait.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + }, + modifier = Modifier.weight(1f).heightIn(min = TouchMinHeight), + ) { Text(tr("members.cropAndUploadPhoto", "Foto wählen & zuschneiden")) } + } + Spacer(modifier = Modifier.height(8.dp)) + } + val avatarUrl = memberProfileImagePath(clubId, member.id)?.let { dependencies.apiConfig.toAbsoluteUrl(it) } + if (avatarUrl != null) { + AuthenticatedAsyncImage( + imageUrl = avatarUrl, + authHeaders = dependencies.diaryAuthHeaders(), + modifier = Modifier + .fillMaxWidth() + .heightIn(max = 220.dp), + contentDescription = member.fullName(), + ) + Spacer(modifier = Modifier.height(12.dp)) + } + DetailLine(tr("mobile.status", "Status"), if (member.active) tr("mobile.active", "Aktiv") else tr("mobile.inactive", "Inaktiv")) + member.testMembership?.takeIf { it }?.let { + DetailLine(tr("members.testMember", "Testmitglied"), tr("mobile.yes", "Ja")) + } + DetailLine(tr("members.street", "Straße"), member.street ?: "-") + DetailLine(tr("members.city", "Ort"), member.city ?: "-") + DetailLine(tr("members.postalCode", "PLZ"), member.postalCode ?: "-") + DetailLine(tr("members.birthDate", "Geburtsdatum"), member.birthDate ?: "-") + DetailLine(tr("members.gender", "Geschlecht"), member.gender ?: "-") + DetailLine("TTR", member.ttr?.toString() ?: "-") + DetailLine("QTTR", member.qttr?.toString() ?: "-") + DetailLine(tr("mobile.lastTraining", "Letztes Training"), member.lastTraining?.take(10) ?: "-") + DetailLine(tr("members.trainingParticipations", "Trainings-Teilnahmen"), member.trainingParticipations?.toString() ?: "-") + + SectionTitle(tr("members.contact", "Kontakt")) + val phones = member.contacts.filter { it.type == "phone" } + val emails = member.contacts.filter { it.type == "email" } + if (phones.isEmpty() && emails.isEmpty()) { + DetailLine(tr("members.phone", "Telefon"), member.phone?.ifBlank { "-" } ?: "-") + DetailLine(tr("members.email", "E-Mail"), member.email?.ifBlank { "-" } ?: "-") + } else { + phones.forEach { DetailLine(tr("members.phone", "Telefon"), it.value) } + emails.forEach { DetailLine(tr("members.email", "E-Mail"), it.value) } + } + + SectionTitle(tr("members.trainingGroups", "Trainingsgruppen")) + if (detailBusy) LoadingInline() + ErrorText(detailError) + memberGroups.forEach { g -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text("· ${g.name}", modifier = Modifier.weight(1f)) + if (canWriteMembers) { + TextButton(onClick = { + dependencies.applicationScope.launch { + runCatching { + dependencies.membersManager.removeMemberFromTrainingGroup(clubId, g.id, member.id) + memberGroups = dependencies.membersManager.listMemberTrainingGroups(clubId, member.id) + } + } + }) { Text(tr("members.removeFromGroup", "Entfernen")) } + } + } + } + if (memberGroups.isEmpty() && !detailBusy) { + Text(tr("members.noGroupsAssigned", "Keine Gruppe zugewiesen."), style = MaterialTheme.typography.caption) + } + if (canWriteMembers) { + Box { + OutlinedButton(onClick = { groupMenu = true }, modifier = Modifier.fillMaxWidth()) { + Text(tr("members.addToGroup", "Gruppe zuweisen")) + } + DropdownMenu(expanded = groupMenu, onDismissRequest = { groupMenu = false }) { + val assigned = memberGroups.map { it.id }.toSet() + val choices = allGroups.filter { it.id !in assigned } + if (choices.isEmpty()) { + DropdownMenuItem(onClick = { groupMenu = false }) { + Text(tr("members.noMoreGroups", "Keine weiteren Gruppen")) + } + } else { + choices.forEach { g -> + DropdownMenuItem(onClick = { + groupMenu = false + dependencies.applicationScope.launch { + runCatching { + dependencies.membersManager.addMemberToTrainingGroup(clubId, g.id, member.id) + memberGroups = dependencies.membersManager.listMemberTrainingGroups(clubId, member.id) + } + } + }) { Text(g.name) } + } + } + } + } + } + + SectionTitle(tr("members.trainingTimesClub", "Trainingszeiten (Verein)")) + schedule.forEach { g -> + Text(g.name, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 6.dp)) + if (g.trainingTimes.isEmpty()) { + Text("–", style = MaterialTheme.typography.caption, modifier = Modifier.padding(start = 8.dp)) + } else { + g.trainingTimes.sortedWith(compareBy { it.weekday }.thenBy { it.sortOrder }).forEach { tt -> + Text( + "· ${trainingWeekdayLabel(tt.weekday)} ${tt.startTime}–${tt.endTime}", + style = MaterialTheme.typography.caption, + modifier = Modifier.padding(start = 8.dp, top = 2.dp), + ) + } + } + } + + SectionTitle(tr("members.activityStats", "Aktivität (Statistik)")) + Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) { + listOf("month" to "1M", "3months" to "3M", "year" to "1J", "all" to "Alle").forEach { (key, label) -> + TextButton( + onClick = { activityPeriod = key }, + enabled = activityPeriod != key, + ) { Text(label) } + } + } + if (activityStats.isEmpty() && !detailBusy) { + Text("–", style = MaterialTheme.typography.caption) + } else { + activityStats.take(25).forEach { row -> + Text( + "${row.name ?: row.code ?: "?"} — ${row.count}×", + style = MaterialTheme.typography.body2, + modifier = Modifier.padding(vertical = 2.dp), + ) + } + } + + SectionTitle(tr("members.lastParticipations", "Letzte Teilnahmen")) + if (lastParts.isEmpty() && !detailBusy) { + Text("–", style = MaterialTheme.typography.caption) + } else { + lastParts.forEach { p -> + val line = "${displayActivityDate(p.date)} ${p.activityName ?: p.activityFullName ?: ""}" + Text(line, style = MaterialTheme.typography.body2, modifier = Modifier.padding(vertical = 2.dp)) + } + } + } +} + +private data class MemberContactEditRow( + val value: String, + val isParent: Boolean = false, + val parentName: String = "", + val isPrimary: Boolean = false, +) + +private fun memberContactsToSave( + phones: List, + emails: List, +): List = buildList { + phones.forEach { r -> + if (r.value.isNotBlank()) { + add( + MemberContactSetBody( + type = "phone", + value = r.value.trim(), + isParent = r.isParent, + parentName = r.parentName.trim().ifBlank { null }, + isPrimary = r.isPrimary, + ), + ) + } + } + emails.forEach { r -> + if (r.value.isNotBlank()) { + add( + MemberContactSetBody( + type = "email", + value = r.value.trim(), + isParent = r.isParent, + parentName = r.parentName.trim().ifBlank { null }, + isPrimary = r.isPrimary, + ), + ) + } + } +} + +private fun primaryPhoneForLegacy(phones: List): String? = + phones.firstOrNull { it.isPrimary && it.value.isNotBlank() }?.value?.trim() + ?: phones.firstOrNull { it.value.isNotBlank() }?.value?.trim() + +private fun primaryEmailForLegacy(emails: List): String? = + emails.firstOrNull { it.isPrimary && it.value.isNotBlank() }?.value?.trim() + ?: emails.firstOrNull { it.value.isNotBlank() }?.value?.trim() + +@Composable +private fun MemberEditRoute( + clubId: Int, + seedMember: Member?, + dependencies: AppDependencies, + canWriteMembers: Boolean, + onBack: () -> Unit, + onSaved: (Int?) -> Unit, +) { + BackHandler(onBack = onBack) + if (!canWriteMembers) { + Column(Modifier.fillMaxSize().padding(ScreenHorizontalPadding)) { + Text(tr("members.noWritePermission", "Keine Berechtigung zum Bearbeiten.")) + TextButton(onClick = onBack) { Text(tr("mobile.back", "Zurück")) } + } + return + } + val scroll = rememberScrollState() + val mid = seedMember?.id ?: 0 + var firstName by remember(mid) { mutableStateOf(seedMember?.firstName.orEmpty()) } + var lastName by remember(mid) { mutableStateOf(seedMember?.lastName.orEmpty()) } + var street by remember(mid) { mutableStateOf(seedMember?.street.orEmpty()) } + var city by remember(mid) { mutableStateOf(seedMember?.city.orEmpty()) } + var postalCode by remember(mid) { mutableStateOf(seedMember?.postalCode.orEmpty()) } + var birthDate by remember(mid) { mutableStateOf(seedMember?.birthDate.orEmpty()) } + val phoneRows = remember(mid) { + mutableStateListOf().apply { + if (seedMember == null) { + add(MemberContactEditRow("")) + } else { + val fromContacts = seedMember.contacts.filter { it.type == "phone" } + if (fromContacts.isNotEmpty()) { + fromContacts.forEach { c -> + add(MemberContactEditRow(c.value, c.isParent, c.parentName.orEmpty(), c.isPrimary)) + } + } else { + val p = seedMember.phone?.takeIf { it.isNotBlank() } + add(MemberContactEditRow(p ?: "", isPrimary = p != null)) + } + } + } + } + val emailRows = remember(mid) { + mutableStateListOf().apply { + if (seedMember == null) { + add(MemberContactEditRow("")) + } else { + val fromContacts = seedMember.contacts.filter { it.type == "email" } + if (fromContacts.isNotEmpty()) { + fromContacts.forEach { c -> + add(MemberContactEditRow(c.value, c.isParent, c.parentName.orEmpty(), c.isPrimary)) + } + } else { + val e = seedMember.email?.takeIf { it.isNotBlank() } + add(MemberContactEditRow(e ?: "", isPrimary = e != null)) + } + } + } + } + var gender by remember(mid) { mutableStateOf(seedMember?.gender ?: "unknown") } + var active by remember(mid) { mutableStateOf(seedMember?.active ?: true) } + var testMembership by remember(mid) { mutableStateOf(seedMember?.testMembership == true) } + var picsOk by remember(mid) { mutableStateOf(seedMember?.picsInInternetAllowed == true) } + var formHanded by remember(mid) { mutableStateOf(seedMember?.memberFormHandedOver == true) } + var adultRelease by remember(mid) { mutableStateOf(seedMember?.adultReleaseApproved == true) } + var adultReserve by remember(mid) { mutableStateOf(seedMember?.adultReserveApproved == true) } + var genderMenu by remember { mutableStateOf(false) } + var saving by remember { mutableStateOf(false) } + var saveError by remember { mutableStateOf(null) } + + fun setPhonePrimary(index: Int) { + val hasValue = phoneRows.getOrNull(index)?.value?.isNotBlank() == true + for (i in phoneRows.indices) { + phoneRows[i] = phoneRows[i].copy(isPrimary = hasValue && i == index) + } + } + + fun setEmailPrimary(index: Int) { + val hasValue = emailRows.getOrNull(index)?.value?.isNotBlank() == true + for (i in emailRows.indices) { + emailRows[i] = emailRows[i].copy(isPrimary = hasValue && i == index) + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scroll) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 12.dp), + ) { + ScreenTopBar( + title = if (seedMember == null) tr("members.newMember", "Neues Mitglied") else tr("members.editMember", "Mitglied bearbeiten"), + onBack = onBack, + ) + ErrorText(saveError) + OutlinedTextField(firstName, { firstName = it }, label = { Text(tr("members.firstName", "Vorname")) }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField(lastName, { lastName = it }, label = { Text(tr("members.lastName", "Nachname")) }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField(street, { street = it }, label = { Text(tr("members.street", "Straße")) }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField(postalCode, { postalCode = it }, label = { Text(tr("members.postalCode", "PLZ")) }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField(city, { city = it }, label = { Text(tr("members.city", "Ort")) }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField(birthDate, { birthDate = it }, label = { Text(tr("members.birthDate", "Geburtsdatum")) }, modifier = Modifier.fillMaxWidth(), singleLine = true) + Box { + OutlinedButton(onClick = { genderMenu = true }, modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) { + Text("${tr("members.gender", "Geschlecht")}: $gender") + } + DropdownMenu(expanded = genderMenu, onDismissRequest = { genderMenu = false }) { + listOf("unknown", "male", "female", "diverse").forEach { g -> + DropdownMenuItem(onClick = { gender = g; genderMenu = false }) { Text(g) } + } + } + } + + SectionTitle(tr("members.phonesSection", "Telefonnummern")) + phoneRows.forEachIndexed { index, row -> + key("phone", index) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp), + elevation = 1.dp, + ) { + Column(modifier = Modifier.padding(10.dp)) { + OutlinedTextField( + value = row.value, + onValueChange = { v -> + val cur = phoneRows[index] + phoneRows[index] = cur.copy(value = v) + }, + label = { Text(tr("members.phone", "Telefon")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Checkbox( + checked = row.isParent, + onCheckedChange = { c -> + val cur = phoneRows[index] + phoneRows[index] = cur.copy(isParent = c) + }, + ) + Text(tr("members.contactIsParent", "Eltern-Telefon"), style = MaterialTheme.typography.body2) + } + if (row.isParent) { + OutlinedTextField( + value = row.parentName, + onValueChange = { v -> + val cur = phoneRows[index] + phoneRows[index] = cur.copy(parentName = v) + }, + label = { Text(tr("members.parentName", "Name des Erziehungsberechtigten")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + } + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Checkbox( + checked = row.isPrimary, + onCheckedChange = { c -> + if (c) setPhonePrimary(index) else { + val cur = phoneRows[index] + phoneRows[index] = cur.copy(isPrimary = false) + } + }, + ) + Text(tr("members.contactPrimary", "Primär"), style = MaterialTheme.typography.body2) + } + if (phoneRows.size > 1) { + TextButton(onClick = { if (phoneRows.size > 1) phoneRows.removeAt(index) }) { + Text(tr("members.removePhoneRow", "Nummer entfernen")) + } + } + } + } + } + } + OutlinedButton( + onClick = { phoneRows.add(MemberContactEditRow("")) }, + modifier = Modifier.fillMaxWidth(), + ) { Text(tr("members.addPhone", "Weitere Telefonnummer")) } + + SectionTitle(tr("members.emailsSection", "E-Mail-Adressen")) + emailRows.forEachIndexed { index, row -> + key("email", index) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp), + elevation = 1.dp, + ) { + Column(modifier = Modifier.padding(10.dp)) { + OutlinedTextField( + value = row.value, + onValueChange = { v -> + val cur = emailRows[index] + emailRows[index] = cur.copy(value = v) + }, + label = { Text(tr("members.email", "E-Mail")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Checkbox( + checked = row.isParent, + onCheckedChange = { c -> + val cur = emailRows[index] + emailRows[index] = cur.copy(isParent = c) + }, + ) + Text(tr("members.contactIsParentEmail", "Eltern-E-Mail"), style = MaterialTheme.typography.body2) + } + if (row.isParent) { + OutlinedTextField( + value = row.parentName, + onValueChange = { v -> + val cur = emailRows[index] + emailRows[index] = cur.copy(parentName = v) + }, + label = { Text(tr("members.parentName", "Name des Erziehungsberechtigten")) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + ) + } + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Checkbox( + checked = row.isPrimary, + onCheckedChange = { c -> + if (c) setEmailPrimary(index) else { + val cur = emailRows[index] + emailRows[index] = cur.copy(isPrimary = false) + } + }, + ) + Text(tr("members.contactPrimary", "Primär"), style = MaterialTheme.typography.body2) + } + if (emailRows.size > 1) { + TextButton(onClick = { if (emailRows.size > 1) emailRows.removeAt(index) }) { + Text(tr("members.removeEmailRow", "Adresse entfernen")) + } + } + } + } + } + } + OutlinedButton( + onClick = { emailRows.add(MemberContactEditRow("")) }, + modifier = Modifier.fillMaxWidth(), + ) { Text(tr("members.addEmail", "Weitere E-Mail")) } + + RowSwitch(tr("mobile.active", "Aktiv"), active, { active = it }) + RowSwitch(tr("members.testMember", "Testmitglied"), testMembership, { testMembership = it }) + RowSwitch(tr("members.picsInternet", "Bilder Internet"), picsOk, { picsOk = it }) + RowSwitch(tr("members.memberFormHandedOver", "Formular ausgehändigt"), formHanded, { formHanded = it }) + RowSwitch(tr("members.adultRelease", "Erwachsenen-Freigabe"), adultRelease, { adultRelease = it }) + RowSwitch(tr("members.adultReserve", "Erw.-Ersatz"), adultReserve, { adultReserve = it }) + if (seedMember != null) { + DetailLine("TTR", seedMember.ttr?.toString() ?: "-") + DetailLine("QTTR", seedMember.qttr?.toString() ?: "-") + } + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { + saving = true + saveError = null + val phoneSnapshot = phoneRows.toList() + val emailSnapshot = emailRows.toList() + val contactsBody = memberContactsToSave(phoneSnapshot, emailSnapshot) + val legacyPhone = primaryPhoneForLegacy(phoneSnapshot).orEmpty() + val legacyEmail = primaryEmailForLegacy(emailSnapshot).orEmpty() + dependencies.applicationScope.launch { + try { + val baseMember = seedMember ?: Member(id = 0, clubId = clubId) + val body = baseMember.toSetBody( + firstname = firstName.trim(), + lastname = lastName.trim(), + street = street.trim().ifBlank { null }, + city = city.trim().ifBlank { null }, + postalCode = postalCode.trim().ifBlank { null }, + birthdate = birthDate.trim().ifBlank { null }, + phone = legacyPhone.ifBlank { null }, + email = legacyEmail.ifBlank { null }, + active = active, + testMembership = testMembership, + picsInInternetAllowed = picsOk, + gender = gender, + memberFormHandedOver = formHanded, + adultReleaseApproved = adultRelease, + adultReserveApproved = adultReserve, + contacts = contactsBody, + ) + dependencies.membersManager.saveMember(clubId, body) + dependencies.membersManager.loadMembers(clubId) + val newId = seedMember?.id ?: dependencies.membersManager.state.value.members + .filter { it.firstName.trim() == firstName.trim() && it.lastName.trim() == lastName.trim() } + .maxByOrNull { it.id }?.id + onSaved(newId) + } catch (t: Throwable) { + saveError = t.message + } finally { + saving = false + } + } + }, + enabled = !saving && firstName.isNotBlank() && lastName.isNotBlank(), + modifier = Modifier.fillMaxWidth().heightIn(min = TouchMinHeight), + ) { Text(tr("common.save", "Speichern")) } + } +} + +@Composable +private fun RowSwitch(label: String, checked: Boolean, onChecked: (Boolean) -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text(label, modifier = Modifier.weight(1f)) + Switch(checked = checked, onCheckedChange = onChecked) + } +} + +@Composable +private fun TrainingStatsScreen(dependencies: AppDependencies) { + val clubState by dependencies.clubManager.state.collectAsState() + val statsState by dependencies.trainingStatsManager.state.collectAsState() + val clubId = clubState.currentClubId ?: return + + LaunchedEffect(clubId) { + dependencies.trainingStatsManager.loadStats(clubId) + } + + Column( + modifier = Modifier + .fillMaxSize() + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp), + ) { + Header(tr("trainingStats.title", "Trainings-Statistik")) + OutlinedButton( + onClick = { dependencies.applicationScope.launch { dependencies.trainingStatsManager.loadStats(clubId) } }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TouchMinHeight), + ) { + Text(tr("mobile.refresh", "Aktualisieren")) + } + if (statsState.isLoading) LoadingInline() + ErrorText(statsState.error) + statsState.stats?.let { stats -> + DetailLine(tr("trainingStats.trainings12Months", "Trainings 12 Monate"), stats.trainingsCount12Months.toString()) + DetailLine(tr("trainingStats.trainings3Months", "Trainings 3 Monate"), stats.trainingsCount3Months.toString()) + DetailLine(tr("members.activeMembers", "Aktive Mitglieder"), stats.overview.activeMembersCount.toString()) + DetailLine(tr("trainingStats.averageParticipants", "Durchschnitt Teilnehmer"), "%.1f".format(stats.overview.averageParticipants12Months)) + SectionTitle(tr("mobile.participationTop", "Top Teilnahmen")) + LazyColumn(modifier = Modifier.weight(1f)) { + items(stats.members.take(20)) { member -> + Text( + "${member.lastName}, ${member.firstName}: ${member.participationTotal}", + modifier = Modifier.padding(vertical = 4.dp), + ) + } + } + } + } +} + +@Composable +private fun SettingsScreen(dependencies: AppDependencies) { + val authState by dependencies.authManager.state.collectAsState() + val clubState by dependencies.clubManager.state.collectAsState() + var sessionStatus by rememberSaveable { mutableStateOf(null) } + var isLoading by rememberSaveable { mutableStateOf(false) } + val sessionValidMessage = tr("mobile.sessionValid", "Session gültig") + val sessionInvalidMessage = tr("mobile.sessionInvalid", "Session ungültig") + val sessionCheckFailedMessage = tr("mobile.sessionCheckFailed", "Session check fehlgeschlagen") + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp), + ) { + Header(tr("mobile.more", "Mehr")) + DetailLine(tr("mobile.user", "Benutzer"), authState.username ?: "-") + DetailLine("Backend", dependencies.apiConfig.baseUrl) + DetailLine("Club", clubState.clubs.firstOrNull { it.id == clubState.currentClubId }?.name ?: "-") + clubState.currentPermissions?.let { permissions -> + DetailLine(tr("mobile.role", "Rolle"), permissions.role) + } + SectionTitle(tr("mobile.language", "Sprache")) + MobileStrings.supportedLanguages.forEach { language -> + TextButton( + onClick = { dependencies.applicationScope.launch { dependencies.languageManager.selectLanguage(language.code) } }, + modifier = Modifier.fillMaxWidth(), + ) { + Text(if (language.code == LocalLanguageCode.current) "✓ ${language.label}" else language.label) + } + } + SectionTitle(tr("mobile.legal", "Rechtliches")) + Text( + tr("mobile.legalBrowserHint", "Öffnet die Web-Oberfläche des konfigurierten Backends im Browser."), + style = MaterialTheme.typography.caption, + modifier = Modifier.padding(bottom = 4.dp), + ) + TextButton( + onClick = { dependencies.openBackendPath("/impressum") }, + modifier = Modifier.fillMaxWidth(), + ) { + Text(tr("impressum.title", "Impressum")) + } + TextButton( + onClick = { dependencies.openBackendPath("/datenschutz") }, + modifier = Modifier.fillMaxWidth(), + ) { + Text(tr("datenschutz.title", "Datenschutz")) + } + Button( + onClick = { + isLoading = true + dependencies.applicationScope.launch { + sessionStatus = runCatching { dependencies.sessionApi.status() } + .fold( + onSuccess = { if (it.valid) sessionValidMessage else sessionInvalidMessage }, + onFailure = { "$sessionCheckFailedMessage: ${it.message}" }, + ) + isLoading = false + } + }, + enabled = !isLoading, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + .heightIn(min = TouchMinHeight), + ) { + Text(tr("mobile.sessionCheck", "Session prüfen")) + } + sessionStatus?.let { Text(it, modifier = Modifier.padding(top = 8.dp)) } + OutlinedButton( + onClick = { + dependencies.applicationScope.launch { + dependencies.authManager.logout() + dependencies.clubManager.clearSelection() + dependencies.diaryManager.clear() + dependencies.membersManager.clear() + dependencies.trainingStatsManager.clear() + } + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + .heightIn(min = TouchMinHeight), + ) { + Text(tr("auth.logout", "Abmelden")) + } + } +} + +@Composable +private fun DiaryEditForm( + submitLabel: String, + onSubmit: (date: String, trainingStart: String?, trainingEnd: String?) -> Unit, + initialDate: String = "", + initialStart: String = "", + initialEnd: String = "", +) { + var date by rememberSaveable { mutableStateOf(initialDate) } + var start by rememberSaveable { mutableStateOf(initialStart) } + var end by rememberSaveable { mutableStateOf(initialEnd) } + var error by rememberSaveable { mutableStateOf(null) } + + Card(modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp), elevation = 2.dp) { + Column(modifier = Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + OutlinedTextField(value = date, onValueChange = { date = it }, label = { Text("Datum (YYYY-MM-DD)") }, singleLine = true) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + OutlinedTextField(value = start, onValueChange = { start = it }, label = { Text("Start") }, modifier = Modifier.weight(1f), singleLine = true) + OutlinedTextField(value = end, onValueChange = { end = it }, label = { Text("Ende") }, modifier = Modifier.weight(1f), singleLine = true) + } + ErrorText(error) + Button(onClick = { + if (!date.matches(Regex("\\d{4}-\\d{2}-\\d{2}"))) { + error = "Datum im Format YYYY-MM-DD eingeben" + return@Button + } + if (start.isNotBlank() && end.isNotBlank() && start >= end) { + error = "Start muss vor Ende liegen" + return@Button + } + error = null + onSubmit(date, start.takeIf { it.isNotBlank() }, end.takeIf { it.isNotBlank() }) + }) { + Text(submitLabel) + } + } + } +} + +@Composable +private fun DiaryListItem(entry: DiaryDate, onClick: () -> Unit) { + Card(modifier = Modifier.fillMaxWidth().clickable(onClick = onClick), elevation = 1.dp) { + Column(modifier = Modifier.padding(12.dp)) { + Text(formatDate(entry.date), fontWeight = FontWeight.SemiBold) + Text(formatTimeRange(entry.trainingStart, entry.trainingEnd)) + if (entry.diaryTags.isNotEmpty()) Text(entry.diaryTags.joinToString(", ") { it.name }) + if (entry.diaryNotes.isNotEmpty()) Text("${entry.diaryNotes.size} Notizen") + } + } +} + +@Composable +private fun FormScaffold( + title: String, + subtitle: String? = null, + content: @Composable ColumnScope.() -> Unit, +) { + val scroll = rememberScrollState() + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scroll) + .imePadding() + .navigationBarsPadding() + .padding(horizontal = ScreenHorizontalPadding, vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text(title, style = MaterialTheme.typography.h5, fontWeight = FontWeight.SemiBold) + subtitle?.let { + Text( + it, + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f), + modifier = Modifier.padding(top = 8.dp), + ) + } + Spacer(modifier = Modifier.height(20.dp)) + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth(), + content = content, + ) + } +} + +@Composable +private fun Header(text: String) { + Text(text, style = MaterialTheme.typography.h5, fontWeight = FontWeight.SemiBold) + Spacer(modifier = Modifier.height(12.dp)) +} + +@Composable +private fun PlanRowMemberAssignExpandable( + clubId: Int, + diaryDateId: Int, + activityApiId: Int, + subtitle: String, + dependencies: AppDependencies, + canWriteDiary: Boolean, + presentMembers: List, + participantRows: List, + expandedId: Int?, + onExpandedIdChange: (Int?) -> Unit, + memberAssignMap: Map>, + onMemberAssignMapChange: (Map>) -> Unit, + busy: Boolean, + onBusyChange: (Boolean) -> Unit, + onParticipantsReplace: (List) -> Unit, +) { + val expanded = expandedId == activityApiId + Column(modifier = Modifier.fillMaxWidth().padding(start = 4.dp, bottom = 8.dp)) { + TextButton( + enabled = !busy, + onClick = { + if (expanded) { + onExpandedIdChange(null) + } else { + onExpandedIdChange(activityApiId) + if (!memberAssignMap.containsKey(activityApiId)) { + dependencies.applicationScope.launch { + onBusyChange(true) + try { + val links = dependencies.diaryManager.listMemberActivityLinks(clubId, activityApiId) + onMemberAssignMapChange( + memberAssignMap + (activityApiId to links.map { it.participantId }.toSet()), + ) + } catch (_: Throwable) { + onMemberAssignMapChange(memberAssignMap + (activityApiId to emptySet())) + } finally { + onBusyChange(false) + } + } + } + } + }, + ) { + Text( + if (expanded) { + tr("diary.hidePlanMemberAssign", "Zuordnung ausblenden") + } else { + "${tr("diary.planMemberAssign", "Wer macht mit?")}: $subtitle" + }, + ) + } + if (expanded) { + if (busy) LoadingInline() + if (presentMembers.isEmpty()) { + Text( + tr("diary.noPresentMembersForAssign", "Keine als anwesend markierten Teilnehmer."), + style = MaterialTheme.typography.caption, + modifier = Modifier.padding(8.dp), + ) + } + presentMembers.forEach { member -> + val rowId = participantRows.find { it.memberId == member.id }?.id + val assigned = rowId != null && (memberAssignMap[activityApiId]?.contains(rowId) == true) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + checked = assigned, + enabled = canWriteDiary && !busy, + onCheckedChange = { want -> + dependencies.applicationScope.launch { + onBusyChange(true) + try { + val pid = dependencies.diaryManager.ensureParticipantRowId(diaryDateId, member.id) + onParticipantsReplace(dependencies.diaryManager.listTrainingParticipants(diaryDateId)) + if (want) { + dependencies.diaryManager.addParticipantsToMemberActivity( + clubId, + activityApiId, + listOf(pid), + ) + } else { + dependencies.diaryManager.removeParticipantFromMemberActivity( + clubId, + activityApiId, + pid, + ) + } + val cur = (memberAssignMap[activityApiId] ?: emptySet()).toMutableSet() + if (want) cur.add(pid) else cur.remove(pid) + onMemberAssignMapChange(memberAssignMap + (activityApiId to cur)) + } finally { + onBusyChange(false) + } + } + }, + ) + Text(member.fullName(), style = MaterialTheme.typography.body2) + } + } + } + } +} + +private fun diaryPlanItemComparator(a: DiaryDateActivityItem, b: DiaryDateActivityItem): Int { + val c = a.orderId.compareTo(b.orderId) + if (c != 0) return c + return a.id.compareTo(b.id) +} + +private fun sameTrainingPlanScope(items: List, item: DiaryDateActivityItem): List { + return items.filter { it.groupId == item.groupId }.sortedWith(::diaryPlanItemComparator) +} + +@Composable +private fun DiaryPlanEditableCard( + item: DiaryDateActivityItem, + allPlanItems: List, + planGroups: List, + planMutating: Boolean, + canWriteDiary: Boolean, + mainImageUrl: String?, + nestedImageUrl: String?, + canReadImages: Boolean, + onOpenImage: (String) -> Unit, + onEdit: () -> Unit, + onDelete: () -> Unit, + onMoveUp: () -> Unit, + onMoveDown: () -> Unit, + onDeleteNested: (Int) -> Unit, +) { + val scope = remember(item.id, allPlanItems) { sameTrainingPlanScope(allPlanItems, item) } + val idx = scope.indexOfFirst { it.id == item.id } + val canUp = idx > 0 + val canDown = idx >= 0 && idx < scope.lastIndex + + val timeblockLabel = tr("diary.timeblock", "Zeitblock") + val title = item.displayTitle(timeblockLabel).ifBlank { + "${tr("mobile.activityFallback", "Aktivität")} ${item.id}" + } + val duration = when { + !item.durationText.isNullOrBlank() -> item.durationText + item.duration != null -> "${item.duration} min" + else -> null + } + val groupLine = item.planGroup?.name?.takeIf { it.isNotBlank() } + ?: item.groupId?.let { gid -> planGroups.find { it.id == gid }?.name ?: "Gruppe $gid" } + val showImageUrl = mainImageUrl ?: nestedImageUrl + + Card( + modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), + elevation = 1.dp, + ) { + Column(modifier = Modifier.padding(12.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Column(modifier = Modifier.weight(1f)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text(title, fontWeight = FontWeight.SemiBold) + if (item.isTimeblock) { + Text( + " · $timeblockLabel", + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.primary, + ) + } + } + groupLine?.let { + Text( + it, + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f), + ) + } + } + if (duration != null) { + Text(duration, style = MaterialTheme.typography.caption) + } + } + if (canReadImages && showImageUrl != null) { + TextButton( + onClick = { onOpenImage(showImageUrl) }, + enabled = !planMutating, + modifier = Modifier.padding(top = 4.dp), + ) { + Text(tr("diary.planShowImage", "Übungsbild anzeigen")) + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + ) { + TextButton(onClick = onMoveUp, enabled = canWriteDiary && !planMutating && canUp) { + Text(tr("diary.planMoveUp", "↑")) + } + TextButton(onClick = onMoveDown, enabled = canWriteDiary && !planMutating && canDown) { + Text(tr("diary.planMoveDown", "↓")) + } + TextButton(onClick = onEdit, enabled = canWriteDiary && !planMutating) { + Text(tr("common.edit", "Bearbeiten")) + } + TextButton(onClick = onDelete, enabled = canWriteDiary && !planMutating) { + Text(tr("common.delete", "Löschen")) + } + } + item.groupActivities + .sortedBy { it.orderId ?: it.id ?: 0 } + .forEach { ga -> + val sub = ga.groupPredefinedActivity.displayLabel() + val line = if (sub.isNotEmpty()) sub else "${tr("mobile.activityFallback", "Aktivität")} ${ga.id}" + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 6.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + "· $line", + style = MaterialTheme.typography.caption, + modifier = Modifier.weight(1f), + ) + val nid = ga.id + if (nid != null) { + TextButton( + onClick = { onDeleteNested(nid) }, + enabled = canWriteDiary && !planMutating, + ) { Text(tr("common.delete", "Löschen")) } + } + } + } + } + } +} + +@Composable +private fun SectionTitle(text: String) { + Spacer(modifier = Modifier.height(16.dp)) + Divider() + Text(text, style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 8.dp)) +} + +@Composable +private fun DetailLine(label: String, value: String) { + Row(modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp)) { + Text(label, modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold) + Text(value, modifier = Modifier.weight(1f)) + } +} + +@Composable +private fun ErrorText(message: String?) { + if (!message.isNullOrBlank()) { + Text(message, color = MaterialTheme.colors.error, modifier = Modifier.padding(vertical = 8.dp)) + } +} + +@Composable +private fun LoadingScreen(label: String) { + Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) { + CircularProgressIndicator() + Text(label, modifier = Modifier.padding(top = 16.dp), style = MaterialTheme.typography.subtitle1) + Text( + tr("mobile.loadingHint", "Ein Moment bitte …"), + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f), + modifier = Modifier.padding(top = 8.dp, start = 32.dp, end = 32.dp), + ) + } +} + +@Composable +private fun LoadingInline() { + Row(modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp), horizontalArrangement = Arrangement.Center) { + CircularProgressIndicator() + } +} + +@Composable +private fun EmptyText(text: String) { + Text(text, modifier = Modifier.padding(vertical = 16.dp)) +} + +@Composable +private fun FlowRowText(items: List) { + if (items.isEmpty()) { + Text("-", modifier = Modifier.padding(vertical = 4.dp)) + } else { + Text(items.joinToString(", "), modifier = Modifier.padding(vertical = 4.dp)) + } +} + +private fun Member.fullName(): String = listOf(firstName, lastName).filter { it.isNotBlank() }.joinToString(" ").ifBlank { "Mitglied $id" } + +private fun formatDate(value: String): String = value.take(10) + +private fun formatTimeRange(start: String?, end: String?): String { + val parts = listOfNotNull(start?.takeIf { it.isNotBlank() }, end?.takeIf { it.isNotBlank() }) + return if (parts.isEmpty()) "Keine Zeiten" else parts.joinToString(" - ") +} + +@Composable +private fun tr(key: String, fallback: String): String = MobileStrings.get(LocalLanguageCode.current, key, fallback) + +@Composable +private fun tabTitle(tab: MainTab): String = when (tab) { + MainTab.Home -> tr("navigation.home", "Start") + MainTab.Diary -> tr("navigation.diary", "Tagebuch") + MainTab.Members -> tr("navigation.members", "Mitglieder") + MainTab.Stats -> tr("navigation.statistics", "Statistik") + MainTab.Settings -> tr("mobile.more", "Mehr") +} diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AuthenticatedAsyncImage.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AuthenticatedAsyncImage.kt new file mode 100644 index 00000000..ab4e13c7 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/AuthenticatedAsyncImage.kt @@ -0,0 +1,35 @@ +package de.tt_tagebuch.app.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import coil.compose.AsyncImage +import coil.request.ImageRequest +import okhttp3.Headers + +@Composable +fun AuthenticatedAsyncImage( + imageUrl: String, + authHeaders: Map, + modifier: Modifier = Modifier, + contentDescription: String? = null, +) { + val context = LocalContext.current + val request = remember(imageUrl, authHeaders) { + val hb = Headers.Builder() + authHeaders.forEach { (k, v) -> hb.add(k, v) } + ImageRequest.Builder(context) + .data(imageUrl) + .headers(hb.build()) + .crossfade(true) + .build() + } + AsyncImage( + model = request, + contentDescription = contentDescription, + modifier = modifier, + contentScale = ContentScale.Fit, + ) +} diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/MemberPortraitCrop.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/MemberPortraitCrop.kt new file mode 100644 index 00000000..609af2d4 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/MemberPortraitCrop.kt @@ -0,0 +1,92 @@ +package de.tt_tagebuch.app.ui + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.FileProvider +import com.yalantis.ucrop.UCrop +import java.io.File + +private fun cropCacheDir(context: Context): File = + File(context.cacheDir, "member_crop").also { it.mkdirs() } + +/** + * Kopiert die gewählte [Uri] in eine Cache-Datei (FileProvider), damit UCrop zuverlässig lesen kann. + */ +fun copyPickedImageToCacheForCrop(context: Context, source: Uri, memberId: Int): Uri { + val dir = cropCacheDir(context) + val inFile = File(dir, "pick-$memberId-${System.currentTimeMillis()}.jpg") + context.contentResolver.openInputStream(source)?.use { input -> + inFile.outputStream().use { output -> input.copyTo(output) } + } + return FileProvider.getUriForFile( + context, + "${context.packageName}.fileprovider", + inFile, + ) +} + +fun createCropOutputUri(context: Context, memberId: Int): Pair { + val dir = cropCacheDir(context) + val outFile = File(dir, "crop-out-$memberId-${System.currentTimeMillis()}.jpg") + if (outFile.exists()) outFile.delete() + outFile.createNewFile() + val uri = FileProvider.getUriForFile( + context, + "${context.packageName}.fileprovider", + outFile, + ) + return outFile to uri +} + +fun buildMemberPortraitCropIntent(context: Context, sourceUri: Uri, destinationUri: Uri): Intent { + val options = UCrop.Options().apply { + setCompressionFormat(Bitmap.CompressFormat.JPEG) + setCompressionQuality(90) + setHideBottomControls(false) + setFreeStyleCropEnabled(true) + } + return UCrop.of(sourceUri, destinationUri) + .withAspectRatio(1f, 1f) + .withMaxResultSize(2048, 2048) + .withOptions(options) + .getIntent(context) + .apply { + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + } +} + +@Composable +fun rememberMemberPortraitCropLauncher( + onCropped: (ByteArray) -> Unit, + onCancelledOrError: (() -> Unit)? = null, +): ActivityResultLauncher { + val context = LocalContext.current + val appContext = remember(context) { context.applicationContext } + return rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode != Activity.RESULT_OK || result.data == null) { + onCancelledOrError?.invoke() + return@rememberLauncherForActivityResult + } + val data = result.data!! + if (UCrop.getError(data) != null) { + onCancelledOrError?.invoke() + return@rememberLauncherForActivityResult + } + val outUri = UCrop.getOutput(data) ?: run { + onCancelledOrError?.invoke() + return@rememberLauncherForActivityResult + } + runCatching { + appContext.contentResolver.openInputStream(outUri)?.use { it.readBytes() } + }.getOrNull()?.let { onCropped(it) } ?: onCancelledOrError?.invoke() + } +} diff --git a/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/TtTheme.kt b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/TtTheme.kt new file mode 100644 index 00000000..921da94b --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/kotlin/de/tt_tagebuch/app/ui/TtTheme.kt @@ -0,0 +1,28 @@ +package de.tt_tagebuch.app.ui + +import androidx.compose.material.MaterialTheme +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +/** Orientierung an `frontend/src/assets/css/main.scss` (:root). */ +object TtAppColors { + val NavRailBackground = Color(0xFFEDF4F0) +} + +private val TtLightColors = lightColors( + primary = Color(0xFF2F7A5F), + primaryVariant = Color(0xFF184636), + secondary = Color(0xFFB56E41), + background = Color(0xFFF4F6F3), + surface = Color(0xFFFFFFFF), + onPrimary = Color.White, + onSecondary = Color.White, + onBackground = Color(0xFF333333), + onSurface = Color(0xFF333333), +) + +@Composable +fun TtTagebuchTheme(content: @Composable () -> Unit) { + MaterialTheme(colors = TtLightColors, content = content) +} diff --git a/mobile-app/composeApp/src/androidMain/res/xml/file_paths.xml b/mobile-app/composeApp/src/androidMain/res/xml/file_paths.xml new file mode 100644 index 00000000..048aac43 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/res/xml/file_paths.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mobile-app/composeApp/src/androidMain/res/xml/network_security_config.xml b/mobile-app/composeApp/src/androidMain/res/xml/network_security_config.xml new file mode 100644 index 00000000..da8bb7d1 --- /dev/null +++ b/mobile-app/composeApp/src/androidMain/res/xml/network_security_config.xml @@ -0,0 +1,9 @@ + + + + + 10.0.2.2 + localhost + 127.0.0.1 + + diff --git a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/DiaryScreen.kt b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/DiaryScreen.kt index d4d50686..67e51a94 100644 --- a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/DiaryScreen.kt +++ b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/DiaryScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.koin.getScreenModel import de.tt_tagebuch.app.viewmodel.DiaryScreenModel @@ -18,6 +19,7 @@ import de.tt_tagebuch.app.viewmodel.DiaryState class DiaryScreen(private val clubId: Int) : Screen { @Composable override fun Content() { + val navigator = LocalNavigator.current val viewModel = getScreenModel() val state by viewModel.state.collectAsState() diff --git a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/HomeScreen.kt b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/HomeScreen.kt index 7f1218b2..378f6c65 100644 --- a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/HomeScreen.kt +++ b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/HomeScreen.kt @@ -1,10 +1,13 @@ package de.tt_tagebuch.app.ui +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.Person import androidx.compose.runtime.* +import androidx.compose.ui.Modifier import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator @@ -30,7 +33,7 @@ class HomeScreen(private val clubId: Int) : Screen { } } ) { padding -> - Box(modifier = androidx.compose.foundation.layout.padding(padding)) { + Box(modifier = Modifier.padding(padding)) { when (selectedItem) { 0 -> DiaryScreen(clubId).Content() 1 -> MemberScreen(clubId).Content() diff --git a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberDetailScreen.kt b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberDetailScreen.kt index 4ecde91e..0cc3d461 100644 --- a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberDetailScreen.kt +++ b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberDetailScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Email import androidx.compose.material.icons.filled.Phone import androidx.compose.runtime.* @@ -35,7 +36,7 @@ class MemberDetailScreen(private val member: Member) : Screen { IconButton(onClick = { navigator?.push(MemberEditScreen(member.clubId, member)) }) { - Icon(androidx.compose.material.icons.filled.Edit, contentDescription = "Edit") + Icon(Icons.Default.Edit, contentDescription = "Edit") } } ) @@ -57,7 +58,7 @@ class MemberDetailScreen(private val member: Member) : Screen { } if (member.testMembership) { Spacer(Modifier.width(8.dp)) - StatusBadge("Test", Color.Orange) + StatusBadge("Test", Color(0xFFFFA500)) } } diff --git a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberEditScreen.kt b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberEditScreen.kt index 402ea285..68b24d3c 100644 --- a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberEditScreen.kt +++ b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberEditScreen.kt @@ -34,7 +34,7 @@ class MemberEditScreen(private val clubId: Int, private val member: Member? = nu var gender by remember { mutableStateOf(member?.gender ?: "unknown") } var active by remember { mutableStateOf(member?.active ?: true) } var testMembership by remember { mutableStateOf(member?.testMembership ?: true) } - var contacts by remember { mutableStateOf(member?.contacts?.toMutableList() ?: mutableListOf()) } + var contacts by remember { mutableStateOf>(member?.contacts?.toMutableList() ?: mutableListOf()) } Scaffold( topBar = { diff --git a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberScreen.kt b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberScreen.kt index 9f56e7a7..92e60b72 100644 --- a/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberScreen.kt +++ b/mobile-app/composeApp/src/commonMain/kotlin/de/tt_tagebuch/app/ui/MemberScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.* +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -67,6 +68,7 @@ class MemberScreen(private val clubId: Int) : Screen { } } + @OptIn(ExperimentalMaterialApi::class) @Composable fun MemberItem(member: de.tt_tagebuch.shared.models.Member, onClick: () -> Unit) { ListItem( diff --git a/mobile-app/gradle.properties b/mobile-app/gradle.properties index 84dafdba..10c98a0c 100644 --- a/mobile-app/gradle.properties +++ b/mobile-app/gradle.properties @@ -32,3 +32,13 @@ org.gradle.configuration-cache=true kotlin.code.style=official android.useAndroidX=true android.nonTransitiveRClass=true + +# Backend für die Standard-App-Installation. +# Hinweis: In Android Studio unter Settings → Build Tools → Gradle können „Command-line options“ +# gesetzt sein (z. B. -PbackendBaseUrl=...) – die überschreiben u. U. diese Zeile. +backendBaseUrl=https://tt-tagebuch.de + +# Temporary workaround for AGP 9.x + Kotlin Multiplatform plugin incompatibility. +# (See AGP error message suggesting these flags.) +android.builtInKotlin=false +android.newDsl=false diff --git a/mobile-app/gradle/gradle-daemon-jvm.properties b/mobile-app/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..5c34300f --- /dev/null +++ b/mobile-app/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/04e088f8677de3b384108493cc9481d0/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/mobile-app/gradle/libs.versions.toml b/mobile-app/gradle/libs.versions.toml index 0914c9ca..e7b7a7e3 100644 --- a/mobile-app/gradle/libs.versions.toml +++ b/mobile-app/gradle/libs.versions.toml @@ -1,34 +1,41 @@ [versions] -agp = "8.2.2" -android-compileSdk = "34" +agp = "9.1.1" +android-compileSdk = "35" android-minSdk = "24" -android-targetSdk = "34" +android-targetSdk = "35" androidx-activityCompose = "1.8.2" androidx-appcompat = "1.6.1" androidx-constraintlayout = "2.1.4" androidx-core-ktx = "1.12.0" +androidx-security-crypto = "1.1.0-alpha06" androidx-espresso-core = "3.5.1" androidx-material = "1.11.0" androidx-test-junit = "1.1.5" compose = "1.6.1" -compose-plugin = "1.6.1" +compose-plugin = "1.10.3" junit = "4.13.2" -kotlin = "1.9.23" +kotlin = "2.1.21" ktor = "2.3.10" coroutines = "1.8.0" koin = "3.5.3" voyager = "1.0.0" socket-io = "2.1.0" +coil = "2.6.0" +ucrop = "2.2.11" [libraries] kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" } +androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "androidx-security-crypto" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } +ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } @@ -38,6 +45,8 @@ voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", vers voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" } voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" } socket-io-client = { module = "io.socket:socket.io-client", version.ref = "socket-io" } +coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } +yalantis-ucrop = { module = "com.github.yalantis:ucrop", version.ref = "ucrop" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } diff --git a/mobile-app/gradle/wrapper/gradle-wrapper.jar b/mobile-app/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8bdaf60c75ab801e22807dde59e12a8735a34077 GIT binary patch literal 45457 zcma&NW0YlEwk;ePwr$(aux;D69T}N{9ky*d!_2U4+qUuIRNZ#Jck8}7U+vcB{`IjNZqX3eq5;s6ddAkU&5{L|^Ow`ym2B0m+K02+~Q)i807X3X94qi>j)C0e$=H zm31v`=T&y}ACuKx7G~yWSYncG=NFB>O2);i9EmJ(9jSamq?Crj$g~1l3m-4M7;BWn zau2S&sSA0b0Rhg>6YlVLQa;D#)1yw+eGs~36Q$}5?avIRne3TQZXb<^e}?T69w<9~ zUmx1cG0uZ?Kd;Brd$$>r>&MrY*3$t^PWF1+J+G_xmpHW=>mly$<>~wHH+Bt3mzN7W zhR)g{_veH6>*KxLJ~~s{9HZm!UeC86d_>42NRqd$ev8zSMq4kt)q*>8kJ8p|^wuKx zq2Is_HJPoQ_apSoT?zJj7vXBp!xejBc^7F|zU0rhy%Ub*Dy#jJs!>1?CmJ-gulPVX zKit>RVmjL=G?>jytf^U@mfnC*1-7EVag@%ROu*#kA+)Rxq?MGK0v-dp^kM?nyMngb z_poL>GLThB7xAO*I7&?4^Nj`<@O@>&0M-QxIi zD@n}s%CYI4Be19C$lAb9Bbm6!R{&A;=yh=#fnFyb`s7S5W3?arZf?$khCwkGN!+GY~GT8-`!6pFr zbFBVEF`kAgtecfjJ`flN2Z!$$8}6hV>Tu;+rN%$X^t8fI>tXQnRn^$UhXO8Gu zt$~QON8`doV&{h}=2!}+xJKrNPcIQid?WuHUC-i%P^F(^z#XB`&&`xTK&L+i8a3a@ zkV-Jy;AnyQ`N=&KONV_^-0WJA{b|c#_l=v!19U@hS~M-*ix16$r01GN3#naZ|DxY2 z76nbjbOnFcx4bKbEoH~^=EikiZ)_*kOb>nW6>_vjf-UCf0uUy~QBb7~WfVO6qN@ns zz=XEG0s5Yp`mlmUad)8!(QDgIzY=OK%_hhPStbyYYd|~zDIc3J4 zy9y%wZOW>}eG4&&;Z>vj&Mjg+>4gL! z(@oCTFf-I^54t=*4AhKRoE-0Ky=qg3XK2Mu!Bmw@z>y(|a#(6PcfbVTw-dUqyx4x4 z3O#+hW1ANwSv-U+9otHE#U9T>(nWx>^7RO_aI>${jvfZQ{mUwiaxHau!H z0Nc}ucJu+bKux?l!dQ2QA(r@(5KZl(Or=U!=2K*8?D=ZT-IAcAX!5OI3w@`sF@$($ zbDk0p&3X0P%B0aKdijO|s})70K&mk1DC|P##b=k@fcJ|lo@JNWRUc>KL?6dJpvtSUK zxR|w8Bo6K&y~Bd}gvuz*3z z@sPJr{(!?mi@okhudaM{t3gp9TJ!|@j4eO1C&=@h#|QLCUKLaKVL z!lls$%N&ZG7yO#jK?U>bJ+^F@K#A4d&Jz4boGmptagnK!Qu{Ob>%+60xRYK>iffd_ z>6%0K)p!VwP$^@Apm%NrS6TpKJwj_Q=k~?4=_*NIe~eh_QtRaqX4t-rJAGYdB{pGq zSXX)-dR8mQ)X|;8@_=J6Dk7MfMp;x)^aZeCtScHs12t3vL+p-6!qhPkOM1OYQ z8YXW5tWp)Th(+$m7SnV_hNGKAP`JF4URkkNc@YV9}FK$9k zR&qgi$Cj#4bC1VK%#U)f%(+oQJ+EqvV{uAq1YG0riLvGxW@)m;*ayU-BSW61COFy0 z(-l>GJqYl;*x1PnRZ(p3Lm}* zlkpWyCoYtg9pAZ5RU^%w=vN{3Y<6WImxj(*SCcJsFj?o6CZ~>cWW^foliM#qN#We{ zwsL!u1$rzC1#4~bILZm*a!T{^kCci$XOJADm)P;y^%x5)#G#_!2uNp^S;cE`*ASCn;}H7pP^RRA z6lfXK(r4dy<_}R|(7%Lyo>QFP#s31E8zsYA${gSUykUV@?lyDNF=KhTeF^*lu7C*{ zBCIjy;bIE;9inJ$IT8_jL%)Q{7itmncYlkf2`lHl(gTwD%LmEPo^gskydVxMd~Do` zO8EzF!yn!r|BEgPjhW#>g(unY#n}=#4J;3FD2ThN5LpO0tI2~pqICaFAGT%%;3Xx$ z>~Ng(64xH-RV^Rj4=A_q1Ee8kcF}8HN{5kjYX0ADh}jq{q18x(pV!23pVsK5S}{M#p8|+LvfKx|_3;9{+6cu7%5o-+R@z>TlTft#kcJ`s2-j zUe4dgpInZU!<}aTGuwgdWJZ#8TPiV9QW<-o!ibBn&)?!ZDomECehvT7GSCRyF#VN2&5GShch9*}4p;8TX~cW*<#( zv-HmU7&+YUWO__NN3UbTFJ&^#3vxW4U9q5=&ORa+2M$4rskA4xV$rFSEYBGy55b{z z!)$_fYXiY?-GWDhGZXgTw}#ilrw=BiN(DGO*W7Vw(} zjUexksYLt_Nq?pl_nVa@c1W#edQKbT>VSN1NK?DulHkFpI-LXl7{;dl@z0#v?x%U& z8k8M1X6%TwR4BQ_eEWJASvMTy?@fQubBU__A_US567I-~;_VcX^NJ-E(ZPR^NASj1 zVP!LIf8QKtcdeH#w6ak50At)e={eF_Ns6J2Iko6dn8Qwa6!NQHZMGsD zhzWeSFK<{hJV*!cIHxjgR+e#lkUHCss-j)$g zF}DyS531TUXKPPIoePo{yH%qEr-dLMOhv^sC&@9YI~uvl?rBp^A-57{aH_wLg0&a|UxKLlYZQ24fpb24Qjil`4OCyt0<1eu>5i1Acv zaZtQRF)Q;?Aw3idg;8Yg9Cb#)03?pQ@O*bCloG zC^|TnJl`GXN*8iI;Ql&_QIY0ik}rqB;cNZ-qagp=qmci9eScHsRXG$zRNdf4SleJ} z7||<#PCW~0>3u8PP=-DjNhD(^(B0AFF+(oKOiQyO5#v4nI|v_D5@c2;zE`}DK!%;H zUn|IZ6P;rl*5`E(srr6@-hpae!jW=-G zC<*R?RLwL;#+hxN4fJ!oP4fX`vC3&)o!#l4y@MrmbmL{t;VP%7tMA-&vju_L zhtHbOL4`O;h*5^e3F{b9(mDwY6JwL8w`oi28xOyj`pVo!75hngQDNg7^D$h4t&1p2 ziWD_!ap3GM(S)?@UwWk=Szym^eDxSx3NaR}+l1~(@0car6tfP#sZRTb~w!WAS{+|SgUN3Tv`J4OMf z9ta_f>-`!`I@KA=CXj_J>CE7T`yGmej0}61sE(%nZa1WC_tV6odiysHA5gzfWN-`uXF46mhJGLpvNTBmx$!i zF67bAz~E|P{L6t1B+K|Cutp&h$fDjyq9JFy$7c_tB(Q$sR)#iMQH3{Og1AyD^lyQwX6#B|*ecl{-_;*B>~WSFInaRE_q6 zpK#uCprrCb`MU^AGddA#SS{P7-OS9h%+1`~9v-s^{s8faWNpt*Pmk_ECjt(wrpr{C_xdAqR(@!ERTSs@F%^DkE@No}wqol~pS^e7>ksF_NhL0?6R4g`P- zk8lMrVir~b(KY+hk5LQngwm`ZQT5t1^7AzHB2My6o)_ejR0{VxU<*r-Gld`l6tfA` zKoj%x9=>Ce|1R|1*aC}|F0R32^KMLAHN}MA<8NNaZ^j?HKxSwxz`N2hK8lEb{jE0& zg4G_6F@#NyDN?=i@=)eidKhlg!nQoA{`PgaH{;t|M#5z}a`u?^gy{5L~I2smLR z*4RmNxHqf9>D>sXSemHK!h4uPwMRb+W`6F>Q6j@isZ>-F=)B2*sTCD9A^jjUy)hjAw71B&$u}R(^R; zY9H3k8$|ounk>)EOi_;JAKV8U8ICSD@NrqB!&=)Ah_5hzp?L9Sw@c>>#f_kUhhm=p z1jRz8X7)~|VwO(MF3PS(|CL++1n|KT3*dhGjg!t_vR|8Yg($ z+$S$K=J`K6eG#^(J54=4&X#+7Car=_aeAuC>dHE+%v9HFu>r%ry|rwkrO-XPhR_#K zS{2Unv!_CvS7}Mb6IIT$D4Gq5v$Pvi5nbYB+1Yc&RY;3;XDihlvhhIG6AhAHsBYsm zK@MgSzs~y|+f|j-lsXKT0(%E2SkEb)p+|EkV5w8=F^!r1&0#0^tGhf9yPZ)iLJ^ zIXOg)HW_Vt{|r0W(`NmMLF$?3ZQpq+^OtjR-DaVLHpz%1+GZ7QGFA?(BIqBlVQ;)k zu)oO|KG&++gD9oL7aK4Zwjwi~5jqk6+w%{T$1`2>3Znh=OFg|kZ z>1cn>CZ>P|iQO%-Pic8wE9c*e%=3qNYKJ+z1{2=QHHFe=u3rqCWNhV_N*qzneN8A5 zj`1Ir7-5`33rjDmyIGvTx4K3qsks(I(;Kgmn%p#p3K zn8r9H8kQu+n@D$<#RZtmp$*T4B&QvT{K&qx(?>t@mX%3Lh}sr?gI#vNi=vV5d(D<=Cp5-y!a{~&y|Uz*PU{qe zI7g}mt!txT)U(q<+Xg_sSY%1wVHy;Dv3uze zJ>BIdSB2a|aK+?o63lR8QZhhP)KyQvV`J3)5q^j1-G}fq=E4&){*&hiam>ssYm!ya z#PsY0F}vT#twY1mXkGYmdd%_Uh12x0*6lN-HS-&5XWbJ^%su)-vffvKZ%rvLHVA<; zJP=h13;x?$v30`T)M)htph`=if#r#O5iC^ZHeXc6J8gewn zL!49!)>3I-q6XOZRG0=zjyQc`tl|RFCR}f-sNtc)I^~?Vv2t7tZZHvgU2Mfc9$LqG z!(iz&xb=q#4otDBO4p)KtEq}8NaIVcL3&pbvm@0Kk-~C@y3I{K61VDF_=}c`VN)3P z+{nBy^;=1N`A=xH$01dPesY_na*zrcnssA}Ix60C=sWg9EY=2>-yH&iqhhm28qq9Z z;}znS4ktr40Lf~G@6D5QxW&?q^R|=1+h!1%G4LhQs54c2Wo~4% zCA||d==lv2bP=9%hd0Dw_a$cz9kk)(Vo}NpSPx!vnV*0Bh9$CYP~ia#lEoLRJ8D#5 zSJS?}ABn1LX>8(Mfg&eefX*c0I5bf4<`gCy6VC{e>$&BbwFSJ0CgVa;0-U7=F81R+ zUmzz&c;H|%G&mSQ0K16Vosh?sjJW(Gp+1Yw+Yf4qOi|BFVbMrdO6~-U8Hr|L@LHeZ z0ALmXHsVm137&xnt#yYF$H%&AU!lf{W436Wq87nC16b%)p?r z70Wua59%7Quak50G7m3lOjtvcS>5}YL_~?Pti_pfAfQ!OxkX$arHRg|VrNx>R_Xyi z`N|Y7KV`z3(ZB2wT9{Dl8mtl zg^UOBv~k>Z(E)O>Z;~Z)W&4FhzwiPjUHE9&T#nlM)@hvAZL>cha-< zQ8_RL#P1?&2Qhk#c9fK9+xM#AneqzE-g(>chLp_Q2Xh$=MAsW z2ScEKr+YOD*R~mzy{bOJjs;X2y1}DVFZi7d_df^~((5a2%p%^4cf>vM_4Sn@@ssVJ z9ChGhs zbanJ+h74)3tWOviXI|v!=HU2mE%3Th$Mpx&lEeGFEBWRy8ogJY`BCXj@7s~bjrOY! z4nIU5S>_NrpN}|waZBC)$6ST8x91U2n?FGV8lS{&LFhHbuHU?SVU{p7yFSP_f#Eyh zJhI@o9lAeEwbZYC=~<(FZ$sJx^6j@gtl{yTOAz`Gj!Ab^y})eG&`Qt2cXdog2^~oOH^K@oHcE(L;wu2QiMv zJuGdhNd+H{t#Tjd<$PknMSfbI>L1YIdZ+uFf*Z=BEM)UPG3oDFe@8roB0h(*XAqRc zoxw`wQD@^nxGFxQXN9@GpkLqd?9@(_ZRS@EFRCO8J5{iuNAQO=!Lo5cCsPtt4=1qZN8z`EA2{ge@SjTyhiJE%ttk{~`SEl%5>s=9E~dUW0uws>&~3PwXJ!f>ShhP~U9dLvE8ElNt3g(6-d zdgtD;rgd^>1URef?*=8BkE&+HmzXD-4w61(p6o~Oxm`XexcHmnR*B~5a|u-Qz$2lf zXc$p91T~E4psJxhf^rdR!b_XmNv*?}!PK9@-asDTaen;p{Rxsa=1E}4kZ*}yQPoT0 zvM}t!CpJvk<`m~^$^1C^o1yM(BzY-Wz2q7C^+wfg-?}1bF?5Hk?S{^#U%wX4&lv0j zkNb)byI+nql(&65xV?_L<0tj!KMHX8Hmh2(udEG>@OPQ}KPtdwEuEb$?acp~yT1&r z|7YU<(v!0as6Xff5^XbKQIR&MpjSE)pmub+ECMZzn7c!|hnm_Rl&H_oXWU2!h7hhf zo&-@cLkZr#eNgUN9>b=QLE1V^b`($EX3RQIyg#45A^=G!jMY`qJ z8qjZ$*-V|?y0=zIM>!2q!Gi*t4J5Otr^OT3XzQ_GjATc(*eM zqllux#QtHhc>YtnswBNiS^t(dTDn|RYSI%i%-|sv1wh&|9jfeyx|IHowW)6uZWR<%n8I}6NidBm zJ>P7#5m`gnXLu;?7jQZ!PwA80d|AS*+mtrU6z+lzms6^vc4)6Zf+$l+Lk3AsEK7`_ zQ9LsS!2o#-pK+V`g#3hC$6*Z~PD%cwtOT8;7K3O=gHdC=WLK-i_DjPO#WN__#YLX|Akw3LnqUJUw8&7pUR;K zqJ98?rKMXE(tnmT`#080w%l1bGno7wXHQbl?QFU=GoK@d!Ov=IgsdHd-iIs4ahcgSj(L@F96=LKZ zeb5cJOVlcKBudawbz~AYk@!^p+E=dT^UhPE`96Q5J~cT-8^tp`J43nLbFD*Nf!w;6 zs>V!5#;?bwYflf0HtFvX_6_jh4GEpa0_s8UUe02@%$w^ym&%wI5_APD?9S4r9O@4m zq^Z5Br8#K)y@z*fo08@XCs;wKBydn+60ks4Z>_+PFD+PVTGNPFPg-V-|``!0l|XrTyUYA@mY?#bJYvD>jX&$o9VAbo?>?#Z^c+Y4Dl zXU9k`s74Sb$OYh7^B|SAVVz*jEW&GWG^cP<_!hW+#Qp|4791Od=HJcesFo?$#0eWD z8!Ib_>H1WQE}shsQiUNk!uWOyAzX>r(-N7;+(O333_ES7*^6z4{`p&O*q8xk{0xy@ zB&9LkW_B}_Y&?pXP-OYNJfqEWUVAPBk)pTP^;f+75Wa(W>^UO_*J05f1k{ zd-}j!4m@q#CaC6mLsQHD1&7{tJ*}LtE{g9LB>sIT7)l^ucm8&+L0=g1E_6#KHfS>A_Z?;pFP96*nX=1&ejZ+XvZ=ML`@oVu>s^WIjn^SY}n zboeP%`O9|dhzvnw%?wAsCw*lvVcv%bmO5M4cas>b%FHd;A6Z%Ej%;jgPuvL$nk=VQ=$-OTwslYg zJQtDS)|qkIs%)K$+r*_NTke8%Rv&w^v;|Ajh5QXaVh}ugccP}3E^(oGC5VO*4`&Q0 z&)z$6i_aKI*CqVBglCxo#9>eOkDD!voCJRFkNolvA2N&SAp^4<8{Y;#Kr5740 za|G`dYGE!9NGU3Ge6C)YByb6Wy#}EN`Ao#R!$LQ&SM#hifEvZp>1PAX{CSLqD4IuO z4#N4AjMj5t2|!yTMrl5r)`_{V6DlqVeTwo|tq4MHLZdZc5;=v9*ibc;IGYh+G|~PB zx2}BAv6p$}?7YpvhqHu7L;~)~Oe^Y)O(G(PJQB<&2AhwMw!(2#AHhjSsBYUd8MDeM z+UXXyV@@cQ`w}mJ2PGs>=jHE{%i44QsPPh(=yorg>jHic+K+S*q3{th6Ik^j=@%xo zXfa9L_<|xTL@UZ?4H`$vt9MOF`|*z&)!mECiuenMW`Eo2VE#|2>2ET7th6+VAmU(o zq$Fz^TUB*@a<}kr6I>r;6`l%8NWtVtkE?}Q<<$BIm*6Z(1EhDtA29O%5d1$0q#C&f zFhFrrss{hOsISjYGDOP*)j&zZUf9`xvR8G)gwxE$HtmKsezo`{Ta~V5u+J&Tg+{bh zhLlNbdzJNF6m$wZNblWNbP6>dTWhngsu=J{);9D|PPJ96aqM4Lc?&6H-J1W15uIpQ ziO{&pEc2}-cqw+)w$`p(k(_yRpmbp-Xcd`*;Y$X=o(v2K+ISW)B1(ZnkV`g4rHQ=s z+J?F9&(||&86pi}snC07Lxi1ja>6kvnut;|Ql3fD)%k+ASe^S|lN69+Ek3UwsSx=2EH)t}K>~ z`Mz-SSVH29@DWyl`ChuGAkG>J;>8ZmLhm>uEmUvLqar~vK3lS;4s<{+ehMsFXM(l- zRt=HT>h9G)JS*&(dbXrM&z;)66C=o{=+^}ciyt8|@e$Y}IREAyd_!2|CqTg=eu}yG z@sI9T;Tjix*%v)c{4G84|0j@8wX^Iig_JsPU|T%(J&KtJ>V zsAR+dcmyT5k&&G{!)VXN`oRS{n;3qd`BgAE9r?%AHy_Gf8>$&X$=>YD7M911?<{qX zkJ;IOfY$nHdy@kKk_+X%g3`T(v|jS;>`pz`?>fqMZ>Fvbx1W=8nvtuve&y`JBfvU~ zr+5pF!`$`TUVsx3^<)48&+XT92U0DS|^X6FwSa-8yviRkZ*@Wu|c*lX!m?8&$0~4T!DB0@)n}ey+ew}T1U>|fH3=W5I!=nfoNs~OkzTY7^x^G&h>M7ewZqmZ=EL0}3#ikWg+(wuoA{7hm|7eJz zNz78l-K81tP16rai+fvXtspOhN-%*RY3IzMX6~8k9oFlXWgICx9dp;`)?Toz`fxV@&m8< z{lzWJG_Y(N1nOox>yG^uDr}kDX_f`lMbtxfP`VD@l$HR*B(sDeE(+T831V-3d3$+% zDKzKnK_W(gLwAK{Saa2}zaV?1QmcuhDu$)#;*4gU(l&rgNXB^WcMuuTki*rt>|M)D zoI;l$FTWIUp}euuZjDidpVw6AS-3dal2TJJaVMGj#CROWr|;^?q>PAo2k^u-27t~v zCv10IL~E)o*|QgdM!GJTaT&|A?oW)m9qk2{=y*7qb@BIAlYgDIe)k(qVH@)#xx6%7 z@)l%aJwz5Joc84Q2jRp71d;=a@NkjSdMyN%L6OevML^(L0_msbef>ewImS=+DgrTk z4ON%Y$mYgcZ^44O*;ctP>_7=}=pslsu>~<-bw=C(jeQ-X`kUo^BS&JDHy%#L32Cj_ zXRzDCfCXKXxGSW9yOGMMOYqPKnU zTF6gDj47!7PoL%z?*{1eyc2IVF*RXX?mj1RS}++hZg_%b@6&PdO)VzvmkXxJ*O7H} z6I7XmJqwX3<>z%M@W|GD%(X|VOZ7A+=@~MxMt8zhDw`yz?V>H%C0&VY+ZZ>9AoDVZeO1c~z$r~!H zA`N_9p`X?z>jm!-leBjW1R13_i2(0&aEY2$l_+-n#powuRO;n2Fr#%jp{+3@`h$c< zcFMr;18Z`UN#spXv+3Ks_V_tSZ1!FY7H(tdAk!v}SkoL9RPYSD3O5w>A3%>7J+C-R zZfDmu=9<1w1CV8rCMEm{qyErCUaA3Q zRYYw_z!W7UDEK)8DF}la9`}8z*?N32-6c-Bwx^Jf#Muwc67sVW24 zJ4nab%>_EM8wPhL=MAN)xx1tozAl zmhXN;*-X%)s>(L=Q@vm$qmuScku>PV(W_x-6E?SFRjSk)A1xVqnml_92fbj0m};UC zcV}lRW-r*wY106|sshV`n#RN{)D9=!>XVH0vMh>od=9!1(U+sWF%#B|eeaKI9RpaW z8Ol_wAJX%j0h5fkvF)WMZ1}?#R(n-OT0CtwsL)|qk;*(!a)5a5ku2nCR9=E*iOZ`9 zy4>LHKt-BgHL@R9CBSG!v4wK zvjF8DORRva)@>nshE~VM@i2c$PKw?3nz(6-iVde;-S~~7R<5r2t$0U8k2_<5C0!$j zQg#lsRYtI#Q1YRs(-%(;F-K7oY~!m&zhuU4LL}>jbLC>B`tk8onRRcmIm{{0cpkD|o@Ixu#x9Wm5J)3oFkbfi62BX8IX1}VTe#{C(d@H|#gy5#Sa#t>sH@8v1h8XFgNGs?)tyF_S^ueJX_-1%+LR`1X@C zS3Oc)o)!8Z9!u9d!35YD^!aXtH;IMNzPp`NS|EcdaQw~<;z`lmkg zE|tQRF7!S!UCsbag%XlQZXmzAOSs= zIUjgY2jcN9`xA6mzG{m|Zw=3kZC4@XY=Bj%k8%D&iadvne$pYNfZI$^2BAB|-MnZW zU4U?*qE3`ZDx-bH})>wz~)a z_SWM!E=-BS#wdrfh;EfPNOS*9!;*+wp-zDthj<>P0a2n?$xfe;YmX~5a;(mNV5nKx zYR86%WtAPsOMIg&*o9uUfD!v&4(mpS6P`bFohPP<&^fZzfA|SvVzPQgbtwwM>IO>Z z75ejU$1_SB1tn!Y-9tajZ~F=Fa~{cnj%Y|$;%z6fJV1XC0080f)Pj|87j142q6`i>#)BCIi+x&jAH9|H#iMvS~?w;&E`y zoarJ)+5HWmZ{&OqlzbdQU=SE3GKmnQq zI{h6f$C@}Mbqf#JDsJyi&7M0O2ORXtEB`#cZ;#AcB zkao0`&|iH8XKvZ_RH|VaK@tAGKMq9x{sdd%p-o`!cJzmd&hb86N!KKxp($2G?#(#BJn5%hF0(^`= z2qRg5?82({w-HyjbffI>eqUXavp&|D8(I6zMOfM}0;h%*D_Dr@+%TaWpIEQX3*$vQ z8_)wkNMDi{rW`L+`yN^J*Gt(l7PExu3_hrntgbW0s}7m~1K=(mFymoU87#{|t*fJ?w8&>Uh zcS$Ny$HNRbT!UCFldTSp2*;%EoW+yhJD8<3FUt8@XSBeJM2dSEz+5}BWmBvdYK(OA zlm`nDDsjKED{$v*jl(&)H7-+*#jWI)W|_X)!em1qpjS_CBbAiyMt;tx*+0P%*m&v< zxV9rlslu8#cS!of#^1O$(ds8aviMFiT`6W+FzMHW{YS+SieJ^?TQb%NT&pasw^kbc znd`=%(bebvrNx3#7vq@vAX-G`4|>cY0svIXopH02{v;GZ{wJM#psz4!m8(IZu<)9D zqR~U7@cz-6H{724_*}-DWwE8Sk+dYBb*O-=c z+wdchFcm6$$^Z0_qGnv0P`)h1=D$_eg8!2-|7Y;o*c)4ax!Me0*EVcioh{wI#!qcb z1&xhOotXMrlo7P6{+C8m;E#4*=8(2y!r0d<6 zKi$d2X;O*zS(&Xiz_?|`ympxITf|&M%^WHp=694g6W@k+BL_T1JtSYX0OZ}o%?Pzu zJ{%P8A$uq?4F!NWGtq>_GLK3*c6dIcGH)??L`9Av&0k$A*14ED9!e9z_SZd3OH6ER zg%5^)3^gw;4DFw(RC;~r`bPJOR}H}?2n60=g4ESUTud$bkBLPyI#4#Ye{5x3@Yw<* z;P5Up>Yn(QdP#momCf=kOzZYzg9E330=67WOPbCMm2-T1%8{=or9L8+HGL{%83lri zODB;Y|LS`@mn#Wmez7t6-x`a2{}U9hE|xY7|BVcFCqoAZQzsEi=dYHB z(bqG3J5?teVSBqTj{aiqe<9}}CEc$HdsJSMp#I;4(EXRy_k|Y8X#5hwkqAaIGKARF zX?$|UO{>3-FU;IlFi80O^t+WMNw4So2nsg}^T1`-Ox&C%Gn_AZ-49Nir=2oYX6 z`uVke@L5PVh)YsvAgFMZfKi{DuSgWnlAaag{RN6t6oLm6{4)H~4xg#Xfcq-e@ALk& z@UP4;uCe(Yjg4jaJZ4pu*+*?4#+XCi%sTrqaT*jNY7|WQ!oR;S8nt)cI27W$Sz!94 z01zoTW`C*P3E?1@6thPe(QpIue$A54gp#C7pmfwRj}GxIw$!!qQetn`nvuwIvMBQ; zfF8K-D~O4aJKmLbNRN1?AZsWY&rp?iy`LP^3KT0UcGNy=Z@7qVM(#5u#Du#w>a&Bs z@f#zU{wk&5n!YF%D11S9*CyaI8%^oX=vq$Ei9cL1&kvv9|8vZD;Mhs1&slm`$A%ED zvz6SQ8aty~`IYp2Xd~G$z%Jf4zwVPKkCtqObrnc2gHKj^jg&-NH|xdNK_;+2d4ZXw zN9j)`jcp7y65&6P@}LsD_OLSi(#GW#hC*qF5KpmeXuQDNS%ZYpuW<;JI<>P6ln!p@ z>KPAM>8^cX|2!n@tV=P)f2Euv?!}UM`^RJ~nTT@W>KC2{{}xXS{}WH{|3najkiEUj z7l;fUWDPCtzQ$?(f)6RvzW~Tqan$bXibe%dv}**BqY!d4J?`1iX`-iy8nPo$s4^mQ z5+@=3xuZAl#KoDF*%>bJ4UrEB2EE8m7sQn!r7Z-ggig`?yy`p~3;&NFukc$`_>?}a z?LMo2LV^n>m!fv^HKKRrDn|2|zk?~S6i|xOHt%K(*TGWkq3{~|9+(G3M-L=;U-YRa zp{kIXZ8P!koE;BN2A;nBx!={yg4v=-xGOMC#~MA07zfR)yZtSF_2W^pDLcXg->*WD zY7Sz5%<_k+lbS^`y)=vX|KaN!gEMQob|(`%nP6huwr$%^?%0^vwr$(CZQD*Jc5?E( zb-q9E`OfoWSJ$rUs$ILfSFg3Mb*-!Ozgaz^%7ZkX@=3km0G;?+e?FQT_l5A9vKr<> z_CoemDo@6YIyl57l*gnJ^7+8xLW5oEGzjLv2P8vj*Q%O1^KOfrsC6eHvk{+$BMLGu z%goP8UY?J7Lj=@jcI$4{m2Sw?1E%_0C7M$lj}w{E#hM4%3QX|;tH6>RJf-TI_1A0w z@KcTEFx(@uitbo?UMMqUaSgt=n`Bu*;$4@cbg9JIS})3#2T;B7S

Z?HZkSa`=MM?n)?|XcM)@e1qmzJ$_4K^?-``~Oi&38`2}sjmP?kK z$yT)K(UU3fJID@~3R;)fU%k%9*4f>oq`y>#t90$(y*sZTzWcW$H=Xv|%^u^?2*n)Csx;35O0v7Nab-REgxDZNf5`cI69k$` zx(&pP6zVxlK5Apn5hAhui}b)(IwZD}D?&)_{_yTL7QgTxL|_X!o@A`)P#!%t9al+# zLD(Rr+?HHJEOl545~m1)cwawqY>cf~9hu-L`crI^5p~-9Mgp9{U5V&dJSwolnl_CM zwAMM1Tl$D@>v?LN2PLe0IZrQL1M zcA%i@Lc)URretFJhtw7IaZXYC6#8slg|*HfUF2Z5{3R_tw)YQ94=dprT`SFAvHB+7 z)-Hd1yE8LB1S+4H7iy$5XruPxq6pc_V)+VO{seA8^`o5{T5s<8bJ`>I3&m%R4cm1S z`hoNk%_=KU2;+#$Y!x7L%|;!Nxbu~TKw?zSP(?H0_b8Qqj4EPrb@~IE`~^#~C%D9k zvJ=ERh`xLgUwvusQbo6S=I5T+?lITYsVyeCCwT9R>DwQa&$e(PxF<}RpLD9Vm2vV# zI#M%ksVNFG1U?;QR{Kx2sf>@y$7sop6SOnBC4sv8S0-`gEt0eHJ{`QSW(_06Uwg*~ zIw}1dZ9c=K$a$N?;j`s3>)AqC$`ld?bOs^^stmYmsWA$XEVhUtGlx&OyziN1~2 z)s5fD(d@gq7htIGX!GCxKT=8aAOHW&DAP=$MpZ)SpeEZhk83}K) z0(Uv)+&pE?|4)D2PX4r6gOGHDY}$8FSg$3eDb*nEVmkFQ#lFpcH~IPeatiH3nPTkP z*xDN7l}r2GM9jwSsl=*!547nRPCS0pb;uE#myTqV+=se>bU=#e)f2}wCp%f-cIrh`FHA$2`monVy?qvJ~o2B6I7IE28bCY4=c#^){*essLG zXUH50W&SWmi{RIG9G^p;PohSPtC}djjXSoC)kyA8`o+L}SjE{i?%;Vh=h;QC{s`T7 zLmmHCr8F}#^O8_~lR)^clv$mMe`e*{MW#Sxd`rDckCnFBo9sC*vw2)dA9Q3lUi*Fy zgDsLt`xt|7G=O6+ms=`_FpD4}37uvelFLc^?snyNUNxbdSj2+Mpv<67NR{(mdtSDNJ3gSD@>gX_7S5 zCD)JP5Hnv!llc-9fwG=4@?=%qu~(4j>YXtgz%gZ#+A9i^H!_R!MxWlFsH(ClP3dU} za&`m(cM0xebj&S170&KLU%39I+XVWOJ_1XpF^ip}3|y()Fn5P@$pP5rvtiEK6w&+w z7uqIxZUj$#qN|<_LFhE@@SAdBy8)xTu>>`xC>VYU@d}E)^sb9k0}YKr=B8-5M?3}d z7&LqQWQ`a&=ihhANxe3^YT>yj&72x#X4NXRTc#+sk;K z=VUp#I(YIRO`g7#;5))p=y=MQ54JWeS(A^$qt>Y#unGRT$0BG=rI(tr>YqSxNm+-x z6n;-y8B>#FnhZX#mhVOT30baJ{47E^j-I6EOp;am;FvTlYRR2_?CjCWY+ypoUD-2S zqnFH6FS+q$H$^7>>(nd^WE+?Zn#@HU3#t|&=JnEDgIU+;CgS+krs+Y8vMo6U zHVkPoReZ-Di3z!xdBu#aW1f{8sC)etjN90`2|Y@{2=Os`(XLL9+ z1$_PE$GgTQrVx`^sx=Y(_y-SvquMF5<`9C=vM52+e+-r=g?D z+E|97MyoaK5M^n1(mnWeBpgtMs8fXOu4Q$89C5q4@YY0H{N47VANA1}M2e zspor6LdndC=kEvxs3YrPGbc;`q}|zeg`f;t3-8na)dGdZ9&d(n{|%mNaHaKJOA~@8 zgP?nkzV-=ULb)L3r`p)vj4<702a5h~Y%byo4)lh?rtu1YXYOY+qyTwzs!59I zL}XLe=q$e<+Wm7tvB$n88#a9LzBkgHhfT<&i#%e*y|}@I z!N~_)vodngB7%CI2pJT*{GX|cI5y>ZBN)}mezK~fFv@$*L`84rb0)V=PvQ2KN}3lTpT@$>a=CP?kcC0S_^PZ#Vd9#CF4 zP&`6{Y!hd^qmL!zr#F~FB0yag-V;qrmW9Jnq~-l>Sg$b%%TpO}{Q+*Pd-@n2suVh_ zSYP->P@# z&gQ^f{?}m(u5B9xqo63pUvDsJDQJi5B~ak+J{tX8$oL!_{Dh zL@=XFzWb+83H3wPbTic+osVp&~UoW3SqK0#P6+BKbOzK65tz)-@AW#g}Ew+pE3@ zVbdJkJ}EM@-Ghxp_4a)|asEk* z5)mMI&EK~BI^aaTMRl)oPJRH^Ld{;1FC&#pS`gh;l3Y;DF*`pR%OSz8U@B@zJxPNX zwyP_&8GsQ7^eYyUO3FEE|9~I~X8;{WTN=DJW0$2OH=3-!KZG=X6TH?>URr(A0l@+d zj^B9G-ACel;yYGZc}G`w9sR$Mo{tzE7&%XKuW$|u7DM<6_z}L>I{o`(=!*1 z{5?1p3F^aBONr6Ws!6@G?XRxJxXt_6b}2%Bp=0Iv5ngnpU^P+?(?O0hKwAK z*|wAisG&8&Td1XY+6qI~-5&+4DE2p|Dj8@do;!40o)F)QuoeUY;*I&QZ0*4?u)$s`VTkNl1WG`}g@J_i zjjmv4L%g&>@U9_|l>8^CN}`@4<D2aMN&?XXD-HNnsVM`irjv$ z^YVNUx3r1{-o6waQfDp=OG^P+vd;qEvd{UUYc;gF0UwaeacXkw32He^qyoYHjZeFS zo(#C9#&NEdFRcFrj7Q{CJgbmDejNS!H%aF6?;|KJQn_*Ps3pkq9yE~G{0wIS*mo0XIEYH zzIiJ>rbmD;sGXt#jlx7AXSGGcjty)5z5lTGp|M#5DCl0q0|~pNQ%1dP!-1>_7^BA~ zwu+uumJmTCcd)r|Hc)uWm7S!+Dw4;E|5+bwPb4i17Ued>NklnnsG+A{T-&}0=sLM- zY;sA9v@YH>b9#c$Vg{j@+>UULBX=jtu~N^%Y#BB5)pB|$?0Mf7msMD<7eACoP1(XY zPO^h5Brvhn$%(0JSo3KFwEPV&dz8(P41o=mo7G~A*P6wLJ@-#|_A z7>k~4&lbqyP1!la!qmhFBfIfT?nIHQ0j2WlohXk^sZ`?8-vwEwV0~uu{RDE^0yfl$ znua{^`VTZ)-h#ch_6^e2{VPaE@o&55|3dx$z_b6gbqduXJ(Lz(zq&ZbJ6qA4Ac4RT zhJO4KBLN!t;h(eW(?cZJw^swf8lP@tWMZ8GD)zg)siA3!2EJYI(j>WI$=pK!mo!Ry z?q&YkTIbTTr<>=}+N8C_EAR0XQL2&O{nNAXb?33iwo8{M``rUHJgnk z8KgZzZLFf|(O6oeugsm<;5m~4N$2Jm5#dph*@TgXC2_k&d%TG0LPY=Fw)=gf(hy9QmY*D6jCAiq44 zo-k2C+?3*+Wu7xm1w*LEAl`Vsq(sYPUMw|MiXrW)92>rVOAse5Pmx^OSi{y%EwPAE zx|csvE{U3c{vA>@;>xcjdCW15pE31F3aoIBsz@OQRvi%_MMfgar2j3Ob`9e@gLQk# zlzznEHgr|Ols%f*a+B-0klD`czi@RWGPPpR1tE@GB|nwe`td1OwG#OjGlTH zfT#^r?%3Ocp^U0F8Kekck6-Vg2gWs|sD_DTJ%2TR<5H3a$}B4ZYpP=p)oAoHxr8I! z1SYJ~v-iP&mNm{ra7!KP^KVpkER>-HFvq*>eG4J#kz1|eu;=~u2|>}TE_5nv2=d!0 z3P~?@blSo^uumuEt{lBsGcx{_IXPO8s01+7DP^yt&>k;<5(NRrF|To2h7hTWBFQ_A z+;?Q$o5L|LlIB>PH(4j)j3`JIb1xA_C@HRFnPnlg{zGO|-RO7Xn}!*2U=Z2V?{5Al z9+iL+n^_T~6Uu{law`R&fFadSVi}da8G>|>D<{(#vi{OU;}1ZnfXy8=etC7)Ae<2S zAlI`&=HkNiHhT0|tQztSLNsRR6v8bmf&$6CI|7b8V4kyJ{=pG#h{1sVeC28&Ho%Fh zwo_FIS}ST-2OF6jNQ$(pjrq)P)@sie#tigN1zSclxJLb-O9V|trp^G8<1rpsj8@+$ z2y27iiM>H8kfd%AMlK|9C>Lkvfs9iSk>k2}tCFlqF~Z_>-uWVQDd$5{3sM%2$du9; z*ukNSo}~@w@DPF)_vS^VaZ)7Mk&8ijX2hNhKom$#PM%bzSA-s$ z0O!broj`!Nuk)Qcp3(>dL|5om#XMx2RUSDMDY9#1|+~fxwP}1I4iYy4j$CGx3jD&eKhf%z`Jn z7mD!y6`nVq%&Q#5yqG`|+e~1$Zkgu!O(~~pWSDTw2^va3u!DOMVRQ8ycq)sk&H%vb z;$a`3gp74~I@swI!ILOkzVK3G&SdTcVe~RzN<+z`u(BY=yuwez{#T3a_83)8>2!X?`^02zVjqx-fN+tW`zCqH^XG>#Ies$qxa!n4*FF0m zxgJlPPYl*q4ylX;DVu3G*I6T&JyWvs`A(*u0+62=+ylt2!u)6LJ=Qe1rA$OWcNCmH zLu7PwMDY#rYQA1!!ONNcz~I^uMvi6N&Lo4dD&HF?1Su5}COTZ-jwR)-zLq=6@bN}X zSP(-MY`TOJ@1O`bLPphMMSWm+YL{Ger>cA$KT~)DuTl+H)!2Lf`c+lZ0ipxd>KfKn zIv;;eEmz(_(nwW24a+>v{K}$)A?=tp+?>zAmfL{}@0r|1>iFQfJ5C*6dKdijK=j16 zQpl4gl93ttF5@d<9e2LoZ~cqkH)aFMgt(el_)#OG4R4Hnqm(@D*Uj>2ZuUCy)o-yy z_J|&S-@o5#2IMcL(}qWF3EL<4n(`cygenA)G%Ssi7k4w)LafelpV5FvS9uJES+(Ml z?rzZ={vYrB#mB-Hd#ID{KS5dKl-|Wh_~v+Lvq3|<@w^MD-RA{q!$gkUUNIvAaex5y z)jIGW{#U=#UWyku7FIAB=TES8>L%Y9*h2N`#Gghie+a?>$CRNth?ORq)!Tde24f5K zKh>cz5oLC;ry*tHIEQEL>8L=zsjG7+(~LUN5K1pT`_Z-4Z}k^m%&H%g3*^e(FDCC{ zBh~eqx%bY?qqu_2qa+9A+oS&yFw^3nLRsN#?FcZvt?*dZhRC_a%Jd{qou(p5AG_Q6 ziOJMu8D~kJ7xEkG(69$Dl3t1J592=Olom%;13uZvYDda08YwzqFlND-;YodmA!SL) z!AOSI=(uCnG#Yo&BgrH(muUemmhQW7?}IHfxI~T`44wuLGFOMdKreQO!a=Z-LkH{T z@h;`A_l2Pp>Xg#`Vo@-?WJn-0((RR4uKM6P2*^-qprHgQhMzSd32@ho>%fFMbp9Y$ zx-#!r8gEu;VZN(fDbP7he+Nu7^o3<+pT!<<>m;m z=FC$N)wx)asxb_KLs}Z^;x*hQM}wQGr((&=%+=#jW^j|Gjn$(qqXwt-o-|>kL!?=T zh0*?m<^>S*F}kPiq@)Cp+^fnKi2)%<-Tw4K3oHwmI-}h}Kc^+%1P!D8aWp!hB@-ZT zybHrRdeYlYulEj>Bk zEIi|PU0eGg&~kWQ{q)gw%~bFT0`Q%k5S|tt!JIZXVXX=>er!7R^w>zeQ%M-(C|eOQG>5i|}i3}X#?aqAg~b1t{-fqwKd(&CyA zmyy)et*E}+q_lEqgbClewiJ=u@bFX}LKe)5o26K9fS;R`!er~a?lUCKf60`4Zq7{2q$L?k?IrAdcDu+ z4A0QJBUiGx&$TBASI2ASM_Wj{?fjv=CORO3GZz;1X*AYY`anM zI`M6C%8OUFSc$tKjiFJ|V74Yj-lK&Epi7F^Gp*rLeDTokfW#o6sl33W^~4V|edbS1 zhx%1PTdnI!C96iYqSA=qu6;p&Dd%)Skjjw0fyl>3k@O?I@x5|>2_7G#_Yc2*1>=^# z|H43bJDx$SS2!vkaMG!;VRGMbY{eJhT%FR{(a+RXDbd4OT?DRoE(`NhiVI6MsUCsT z1gc^~Nv>i;cIm2~_SYOfFpkUvV)(iINXEep;i4>&8@N#|h+_;DgzLqh3I#lzhn>cN zjm;m6U{+JXR2Mi)=~WxM&t9~WShlyA$Pnu+VIW2#;0)4J*C!{1W|y1TP{Q;!tldR< zI7aoH&cMm*apW}~BabBT;`fQ1-9q|!?6nTzmhiIo6fGQlcP{pu)kJh- zUK&Ei9lArSO6ep_SN$Lt_01|Y#@Ksznl@f<+%ku1F|k#Gcwa`(^M<2%M3FAZVb99?Ez4d9O)rqM< zCbYsdZlSo{X#nKqiRA$}XG}1Tw@)D|jGKo1ITqmvE4;ovYH{NAk{h8*Ysh@=nZFiF zmDF`@4do#UDKKM*@wDbwoO@tPx4aExhPF_dvlR&dB5>)W=wG6Pil zq{eBzw%Ov!?D+%8&(uK`m7JV7pqNp-krMd>ECQypq&?p#_3wy){eW{(2q}ij{6bfmyE+-ZO z)G4OtI;ga9;EVyKF6v3kO1RdQV+!*>tV-ditH-=;`n|2T zu(vYR*BJSBsjzFl1Oy#DpL=|pfEY4NM;y5Yly__T*Eg^3Mb_()pHwn)mAsh!7Yz-Z zY`hBLDXS4F^{>x=oOphq|LMo;G!C(b2hS9A6lJqb+e$2af}7C>zW2p{m18@Bdd>iL zoEE$nFUnaz_6p${cMO|;(c1f9nm5G5R;p)m4dcC1?1YD=2Mi&20=4{nu>AV#R^d%A zsmm_RlT#`;g~an9mo#O1dYV)2{mgUWEqb*a@^Ok;ckj;uqy{%*YB^({d{^V)P9VvP zC^qbK&lq~}TWm^RF8d4zbo~bJuw zFV!!}b^4BlJ0>5S3Q>;u*BLC&G6Fa5V|~w&bRZ*-YU>df6%qAvK?%Qf+#=M-+JqLw&w*l4{v7XTstY4j z26z69U#SVzSbY9HBXyD;%P$#vVU7G*Yb-*fy)Qpx?;ed;-P24>-L6U+OAC9Jj63kg zlY`G2+5tg1szc#*9ga3%f9H9~!(^QjECetX-PlacTR+^g8L<#VRovPGvsT)ln3lr= zm5WO@!NDuw+d4MY;K4WJg3B|Sp|WdumpFJO>I2tz$72s4^uXljWseYSAd+vGfjutO z-x~Qlct+BnlI+Iun)fOklxPH?30i&j9R$6g5^f&(x7bIom|FLKq9CUE);w2G>}vye zxWvEaXhx8|~2j)({Rq>0J9}lzdE`yhQ(l$z! z;x%d%_u?^4vlES_>JaIjJBN|N8z5}@l1#PG_@{mh`oWXQOI41_kPG}R_pV+jd^PU) zEor^SHo`VMul*80-K$0mSk|FiI+tHdWt-hzt~S>6!2-!R&rdL_^gGGUzkPe zEZkUKU=EY(5Ex)zeTA4-{Bkbn!Gm?nuaI4jLE%X;zMZ7bwn4FXz(?az;9(Uv;38U6 zi)}rA3xAcD2&6BY<~Pj9Q1~4Dyjs&!$)hyHiiTI@%qXd~+>> zW}$_puSSJ^uWv$jtWakn}}@eX6_LGz|7M#$!3yjY ztS{>HmQ%-8u0@|ig{kzD&CNK~-dIK5e{;@uWOs8$r>J7^c2P~Pwx%QVX0e8~oXK0J zM4HCNK?%t6?v~#;eP#t@tM$@SXRt;(b&kU7uDzlzUuu;+LQ5g%=FqpJPGrX8HJ8CS zITK|(fjhs3@CR}H4@)EjL@J zV_HPexOQ!@k&kvsQG)n;7lZaUh>{87l4NS_=Y-O9Ul3CaKG8iy+xD=QXZSr57a-hb z7jz3Ts-NVsMI783OPEdlE|e&a2;l^h@e>oYMh5@=Lte-9A+20|?!9>Djl~{XkAo>0p9`n&nfWGdGAfT-mSYW z1cvG>GT9dRJdcm7M_AG9JX5AqTCdJ6MRqR3p?+FvMxp(oB-6MZ`lRzSAj%N(1#8@_ zDnIIo9Rtv12(Eo}k_#FILhaZQ`yRD^Vn5tm+IK@hZO>s=t5`@p1#k?Umz2y*R64CF zGM-v&*k}zZ%Xm<_?1=g~<*&3KAy;_^QfccIp~CS7NW24Tn|mSDxb%pvvi}S}(~`2# z3I|kD@||l@lAW06K2%*gHd4x9YKeXWpwU%!ozYcJ+KJeX!s6b94j!Qyy7>S!wb?{qaMa`rpbU1phn0EpF}L zsBdZc|Im#iRiQmJjZwb5#n;`_O{$Zu$I zMXqbfu0yVmt!!Y`Fzl}QV7HUSOPib#da4i@vM$0u2FEYytsvrbR#ui9lrMkZ(AVVJ zMVl^Wi_fSRsEXLA_#rdaG%r(@UCw#o7*yBN)%22b)VSNyng6Lxk|2;XK3Qb=C_<`F zN##8MLHz-s%&O6JE~@P1=iHpj8go@4sC7*AWe99tuf$f7?2~wC&RA^UjB*2`K!%$y zSDzMd7}!vvN|#wDuP%%nuGk8&>N)7eRxtqdMXHD1W%hP7tYW{W>^DJp`3WS>3}i+$ z_li?4AlEj`r=!SPiIc+NNUZ9NCrMv&G0BdQHBO&S7d48aB)LfGi@D%5CC1%)1hVcJ zB~=yNC}LBn(K?cHkPmAX$5^M7JSnNkcc!X!0kD&^F$cJmRP(SJ`9b7}b)o$rj=BZ- zC;BX3IG94%Qz&(V$)7O~v|!=jd-yU1(6wd1u;*$z4DDe6+BFLhz>+8?59?d2Ngxck zm92yR!jk@MP@>>9FtAY2L+Z|MaSp{MnL-;fm}W3~fg!9TRr3;S@ysLf@#<)keHDRO zsJI1tP`g3PNL`2(8hK3!4;r|E-ZQbU0e-9u{(@du`4wjGj|A!QB&9w~?OI1r}M? zw)6tvsknfPfmNijZ;3VZX&HM6=|&W zy6GIe3a?_(pRxdUc==do9?C&v7+6cgIoL4)Ka^bOG9`l;S|QmVzjv%)3^PDi@=-cp z=!R0bU<@_;#*D}e1m@0!%k=VPtyRAkWYW(VFl|eu0LteWH7eDB%P|uF7BQ-|D4`n; z)UpuY1)*s32UwW756>!OoAq#5GAtfrjo*^7YUv^(eiySE?!TQzKxzqXE@jM_bq3Zq zg#1orE*Zd5ZWEpDXW9$=NzuadNSO*NW)ZJ@IDuU`w}j_FRE4-QS*rD4mPVQPH(jGg z+-Ye?3%G%=DT5U1b+TnNHHv(nz-S?3!M4hXtEB@J4WK%%p zkv=Bb`1DHmgUdYo>3kwB(T>Ba#DKv%cLp2h4r8v}p=Np}wL!&PB5J-w4V4REM{kMD z${oSuAw9?*yo3?tNp~X5WF@B^P<6L0HtIW0H7^`R8~9zAXgREH`6H{ntGu$aQ;oNq zig;pB^@KMHNoJcEb0f1fz+!M6sy?hQjof-QoxJgBM`!k^T~cykcmi^s_@1B9 z)t1)Y-ZsV9iA&FDrVoF=L7U#4&inXk{3+Xm9A|R<=ErgxPW~Fq zqu-~x0dIBlR+5_}`IK^*5l3f5$&K@l?J{)_d_*459pvsF*e*#+2guls(cid4!N%DG zl3(2`az#5!^@HNRe3O4(_5nc+){q?ENQG2|uKW0U0$aJ5SQ6hg>G4OyN6os76y%u8qNNHi;}XnRNwpsfn^!6Qt(-4tE`uxaDZ`hQp#aFX373|F?vjEiSEkV>K)cTBG+UL#wDj0_ zM9$H&-86zP=9=5_Q7d3onkqKNr4PAlF<>U^^yYAAEso|Ak~p$3NNZ$~4&kE9Nj^As zQPoo!m*uZ;z1~;#g(?zFECJ$O2@EBy<;F)fnQxOKvH`MojG5T?7thbe%F@JyN^k1K zn3H*%Ymoim)ePf)xhl2%$T)vq3P=4ty%NK)@}po&7Q^~o3l))Zm4<75Y!fFihsXJc z9?vecovF^nYfJVg#W~R3T1*PK{+^YFgb*7}Up2U#)oNyzkfJ#$)PkFxrq_{Ai?0zk zWnjq_ixF~Hs7YS9Y6H&8&k0#2cAj~!Vv4{wCM zi2f1FjQf+F@=BOB)pD|T41a4AEz+8hnH<#_PT#H|Vwm7iQ0-Tw()WMN za0eI-{B2G{sZ7+L+^k@BA)G;mOFWE$O+2nS|DzPSGZ)ede(9%+8kqu4W^wTn!yZPN z7u!Qu0u}K5(0euRZ$7=kn9DZ+llruq5A_l) zOK~wof7_^8Yeh@Qd*=P!gM)lh`Z@7^M?k8Z?t$$vMAuBG>4p56Dt!R$p{)y>QG}it zGG;Ei```7ewXrbGo6Z=!AJNQ!GP8l13m7|FIQTFZTpIg#kpZkl1wj)s1eySXjAAWy zfl;;@{QQ;Qnb$@LY8_Z&7 z6+d98F?z2Zo)sS)z$YoL(zzF>Ey8u#S_%n7)XUX1Pu(>e8gEUU1S;J=EH(#`cWi1+ zoL$5TN+?#NM8=4E7HOk)bf5MXvEo%he5QcB%_5YQ$cu_j)Pd^@5hi}d%nG}x9xXtD-JMQxr;KkC=r_dS-t`lf zF&CS?Lk~>U^!)Y0LZqNVJq+*_#F7W~!UkvZfQhzvW`q;^X&iv~ zEDDGIQ&(S;#Hb(Ej4j+#D#sDS_uHehlY0kZsQpktc?;O z22W1b%wNcdfNza<1M2{*mAkM<{}@(w`VuQ<^lG|iYSuWBD#lYK9+jsdA+&#;Y@=zXLVr840Nq_t5))#7}2s9pK* zg42zd{EY|#sIVMDhg9>t6_Y#O>JoG<{GO&OzTa;iA9&&^6=5MT21f6$7o@nS=w;R) znkgu*7Y{UNPu7B9&B&~q+N@@+%&cO0N`TZ-qQ|@f@e0g2BI+9xO$}NzMOzEbSSJ@v z1uNp(S z-dioXc$5YyA6-My@gW~1GH($Q?;GCHfk{ej-{Q^{iTFs1^Sa67RNd5y{cjX1tG+$& zbGrUte{U1{^Z_qpzW$-V!pJz$dQZrL5i(1MKU`%^= z^)i;xua4w)evDBrFVm)Id5SbXMx2u7M5Df<2L4B`wy4-Y+Wec#b^QJO|J9xF{x#M8 zuLUer`%ZL^m3gy?U&dI+`kgNZ+?bl3H%8)&k84*-=aMfADh&@$xr&IS|4{3$v&K3q zZTn&f{N(#L6<-BZYNs4 zB*Kl*@_IhGXI^_8zfXT^XNmjJ@5E~H*wFf<&er?p7suz85)$-Hqz@C zGMFg1NKs;otNViu)r-u{SOLcqwqc7$poPvm(-^ag1m71}HL#cj5t4Hw(W?*fi4GSH z9962NZ>p^ECPqVc$N}phy>N8rQsWWm%%rc5B4XLATFEtffX&TM2%|8S2Lh_q; zCytXua84HBnSybW-}(j z3Zwv4CaK)jC!{oUvdsFRXK&Sx@t)yGm(h65$!WZ!-jL52no}NX6=E<=H!aZ74h_&> zZ+~c@k!@}Cs84l{u+)%kg4fq~pOeTK3S4)gX~FKJw4t9ba!Ai{_gkKQYQvafZIyKq zX|r4xgC(l%JgmW!tvR&yNt$6uME({M`uNIi7HFiPEQo_UMRkl~12&4c& z^se;dbZWKu7>dLMg`IZq%@b@ME?|@{&xEIZEU(omKNUY? z`JszxNghuO-VA;MrZKEC0|Gi0tz3c#M?aO?WGLy64LkG4T%|PBIt_?bl{C=L@9e;A zia!35TZI7<`R8hr06xF62*rNH5T3N0v^acg+;ENvrLYo|B4!c^eILcn#+lxDZR!%l zjL6!6h9zo)<5GrSPth7+R(rLAW?HF4uu$glo?w1U-y}CR@%v+wSAlsgIXn>e%bc{FE;j@R0AoNIWf#*@BSngZ)HmNqkB z)cs3yN%_PT4f*K+Y1wFl)be=1iq+bb1G-}b|72|gJ|lMt`tf~0Jk}zMbS0+M-Mq}R z>Bv}-W6J%}j#dIz`Z0}zD(DGKn`R;E8A`)$a6qDfr(c@iHKZcCVY_nJEDpcUddGH* z*ct2$&)RelhmV}@jGXY>3Y~vp;b*l9M+hO}&x`e~q*heO8GVkvvJTwyxFetJC8VnhjR`5*+qHEDUNp16g`~$TbdliLLd}AFf}U+Oda1JXwwseRFbj?DN96;VSX~z?JxJSuA^BF}262%Z0)nv<6teKK`F zfm9^HsblS~?Xrb1_~^=5=PD!QH$Y1hD_&qe1HTQnese8N#&C(|Q)CvtAu6{{0Q%ut8ESVdn&& z4y%nsCs!$(#9d{iVjXDR##3UyoMNeY@_W^%qyuZ^K3Oa4(^!tDXOUS?b2P)yRtJ8j zSX}@qGBj+gKf;|6Kb&rq`!}S*cSu-3&S>=pM$eEB{K>PP~I}N|uGE|`3U#{Q6v^kO4nIsaq zfPld}c|4tVPI4!=!ETCNW+LjcbmEoxm0RZ%ieV0`(nVlWKClZW5^>f&h79-~CF(%+ zv|KL(^xQ7$#a}&BSGr9zf{xJ(cCfq>UR*>^-Ou_pmknCt6Y--~!duL{k2D{yLMl__ z!KeMRRg&EsD2s|cmy?xgK&XcGIKeos`&UEVhBTw;mqy|8DlP1M7PYS2z{YmTJ;n!h znPe(Qu?c7+xZz!Tm1AnE8|;&tf7fW$2dArX7ck1Jd(S1+91YB8bjISRZ`UL*?vb{b zMp*!Xq7VaLc0Ogqj5qmop8NREQ{9_iC$;tviZlubGLy1jLlIFBxAymMr@SDLAcx+) z5YRkl$bW**X)W0JzWNcLx9>fTqJj00ipY6Ua?mUlsgQrVVgpmaheE;RgA5U_+WsPh z9+X|PU4zFyNxZ2?Q+V`Mo{xH~(m}OMRZa<&$nCl7o4x`^^|V4?aPz8#KwFm=8T6_} z8=P_4$_rD2a%7}}HT6VQ>ZGKW=QF7zI-2=6oBNZR$HVn|gq`>l$HZ`48lkM7%R$>MS& zghR`WZ9Xrd_6FaDedH6_aKVJhYev*2)UQ>!CRH3PQ_d9nXlO;c z9PeqiKD@aGz^|mvD-tV<{BjfA;)B+76!*+`$CZOJ=#)}>{?!9fAg(Xngbh||n=q*C zU0mGP`NxHn$uY#@)gN<0xr)%Ue80U{-`^FX1~Q@^>WbLraiB|c#4v$5HX)0z!oA#jOXPyWg! z8EC}SBmG7j3T&zCenPLYA{kN(3l62pu}91KOWZl? zg~>T4gQ%1y3AYa^J|>ba$7F5KlVx}_&*~me*q-SYLBCXZFU=U8mHQD4K!?;B61NoX z?VS41SS&jHyhmB~+bC=w0a06V``ZXCkC~}oM9pM{$hU~-s_elYPmT1L!%B`?*<+?( zFQ@TP%y+QL`_&Y0A3679pe5~iL=z)$b)k!oSbJRyw+K};SGAvvE=|<~*aiwJc?uE@2?7a1i9|3=^N%*9smt3ZIhjY>gIsr{Q2rX(NovZ7I1n^V{ z#~(1ze-%`C>fM`^hCV**9BA-04lNuu&3=reevNOMwmX(A{yh`^c8%0mjAKMj{Th05 zXrM(zILwyL-Pcdw^(=gj(ZLVMA95zlzmLa^skb8tQq%8SV&4vp?S>L3+P4^tp`$xA zr38jBw0ItR`VbO5vB1`<3d})}aorkIU1z3*ifYN&Lpp)}|}QJS60th_v-EEkAM zyOREuj!Ou|pVeZEWg;$Hf!x;xAmFu7gB^UR$=L0BuZ~thLC@#moJ(@@wejR|`t_K@ zuQ{XmpAWz%o&~2dk!SIGR$EmpZY)@+r^gvX26%)y>1u2bt~JUPTQzQu&_tB)|{19)&n$m5Fhw0A-8S1^%XpAD%`#a z_ModVxsM|x!m3N1vRt_XEL`O-+J3cMsM1l*dbjT&S0c@}Xxl3I&AeMNT97G3c6%3C zbrZS?2EAKcEq@@Pw?r%eh0YM6z0>&Qe#n+e9hEHK?fzig3v5S#O2IxVLu;a>~c~ZfHVbgLox%_tg)bsC8Rl35P=Jhl+Y=w6zb$ z;*uO%i^U z^mp_QggBILLF$AyjPD41Z0SFdbDj&z&xjq~X|OoM7bCuBfma1CEd!4RKGqPR)K)e}+7^JfFUI_fy63cMyq#&)Z*#w18{S zhC@f9U5k#2S2`d$-)cEoH-eAz{2Qh>YF1Xa)E$rWd52N-@{#lrw3lRqr)z?BGThgO z-Mn>X=RPHQ)#9h{3ciF)<>s{uf_&XdKb&kC!a373l2OCu&y8&n#P%$7YwAVJ_lD-G zX7tgMEV8}dY^mz`R6_0tQ5Eu@CdSOyaI63Vb*mR+rCzxgsjCXLSHOmzt0tA zGoA0Cp&l>rtO@^uQayrkoe#d2@}|?SlQl9W{fmcxY(0*y zHTZ6>FL;$8FEzbb;M(o%mBe-X?o<0+1dH?ZVjcf8)Kyqb07*a zLfP1blbt)=W)TN}4M#dUnt8Gdr4p$QRA<0W)JhWLK3-g82Q~2Drmx4J z;6m4re%igus136VL}MDI-V;WmSfs4guF_(7ifNl#M~Yx5HB!UF)>*-KDQl0U?u4UXV2I*qMhEfsxb%87fi+W;mW5{h?o8!52}VUs*Fpo#aSuXk(Ug z>r>xC#&2<9Uwmao@iJQ|{Vr__?eRT2NB$OcoXQ-jZ{t|?Uy{7q$nU-i|&-R6fHPWJDgHZ69iVbK#Ab@2@y zPD*Gj=hib?PWr8NGf;g$o5I!*n>94Z!IfqRm zLvM>Gx$Y*rEL3Z-+lS42=cnEfXR)h1z`h8a+I%E_ss%qXsrgIV%qv9d|KT>fV5=3e zw>P#ju>2naGc{=6!)9TeHq$S9Pk|>$UCEl}H}lE@;0(jbNT9TXUXyss>al>S4DuGi zVCy;Qt=a2`iu2;TvrIkh2NTvNV}0)qun~9y1yEQMdOf#V#3(e(C?+--8bCsJu={Q1z5qNJIk&yW>ZnVm;A=fL~29lvXQ*4j(SLau?P zi8LC7&**O!6B6=vfY%M;!p2L2tQ+w3Y!am{b?14E`h4kN$1L0XqT5=y=DW8GI_yi% zlIWsjmf0{l#|ei>)>&IM4>jXH)?>!fK?pfWIQn9gT9N(z&w3SvjlD|u*6T@oNQRF6 zU5Uo~SA}ml5f8mvxzX>BGL}c2#AT^6Lo-TM5XluWoqBRin$tiyRQK0wJ!Ro+7S!-K z=S95p-(#IDKOZsRd{l65N(Xae`wOa4Dg9?g|Jx97N-7OfHG(rN#k=yNGW0K$Tia5J zMMX1+!ulc1%8e*FNRV8jL|OSL-_9Nv6O=CH>Ty(W@sm`j=NFa1F3tT$?wM1}GZekB z6F_VLMCSd7(b9T%IqUMo$w9sM5wOA7l8xW<(1w0T=S}MB+9X5UT|+nemtm_;!|bxX z_bnOKN+F30ehJ$459k@=69yTz^_)-hNE4XMv$~_%vlH_y^`P1pLxYF6#_IZyteO`9wpuS> z#%Vyg5mMDt?}j!0}MoBX|9PS0#B zSVo6xLVjujMN57}IVc#A{VB*_yx;#mgM4~yT6wO;Qtm8MV6DX?u(JS~JFA~PvEl%9 z2XI}c>OzPoPn_IoyXa2v}BA(M+sWq=_~L0rZ_yR17I5c^m4;?2&KdCc)3lCs!M|0OzH@(PbG8T6w%N zKzR>%SLxL_C6~r3=xm9VG8<9yLHV6rJOjFHPaNdQHHflp><44l>&;)&7s)4lX%-er znWCv8eJJe1KAi_t1p%c4`bgxD2(1v)jm(gvQLp2K-=04oaIJu{F7SIu8&)gyw7x>+ zbzYF7KXg;T71w!-=C0DjcnF^JP$^o_N>*BAjtH!^HD6t1o?(O7IrmcodeQVDD<*+j zN)JdgB6v^iiJ1q`bZ(^WvN{v@sDqG$M9L`-UV!3q&sWZUnQ{&tAkpX(nZ_L#rMs}>p7l0fU5I5IzArncQi6TWjP#1B=QZ|Uqm-3{)YPn=XFqHW-~Fb z^!0CvIdelQbgcac9;By79%T`uvNhg9tS><pLzXePP=JZzcO@?5GRAdF4)sY*)YGP* zyioMa3=HRQz(v}+cqXc0%2*Q%CQi%e2~$a9r+X*u3J8w^Shg#%4I&?!$})y@ zzg8tQ6_-`|TBa_2v$D;Q(pFutj7@yos0W$&__9$|Yn3DFe*)k{g^|JIV4bqI@2%-4kpb_p? zQ4}qQcA>R6ihbxnVa{c;f7Y)VPV&mRY-*^qm~u3HB>8lf3P&&#GhQk8uIYYgwrugY zei>mp`YdC*R^Cxuv@d0V?$~d*=m-X?1Fqd9@*IM^wQ_^-nQEuc0!OqMr#TeT=8W`JbjjXc-Dh3NhnTj8e82yP;V_B<7LIejij+B{W1ViaJ_)+q?$BaLJpxt_4@&(?rWC3NC-_Z9Sg4JJWc( zX!Y34j67vCMHKB=JcJ1|#UI^D^mn(i=A5rf-iV7y4bR5HhC=I`rFPZv4F>q+h?l34 z4(?KYwZYHwkPG%kK7$A&M#=lpIn3Qo<>s6UFy|J$Zca-s(oM7??dkuKh?f5b2`m57 zJhs4BTcVVmwsswlX?#70uQb*k1Fi3q4+9`V+ikSk{L3K=-5HgN0JekQ=J~549Nd*+H%5+fi6aJuR=K zyD3xW{X$PL7&iR)=wumlTq2gY{LdrngAaPC;Qw_xLfVE0c0Z>y918TQpL!q@?`8{L!el18Qxiki3WZONF=eK$N3)p>36EW)I@Y z7QxbWW_9_7a*`VS&5~4-9!~&g8M+*U9{I2Bz`@TJ@E(YL$l+%<=?FyR#&e&v?Y@@G zqFF`J*v;l$&(A=s`na2>4ExKnxr`|OD+Xd-b4?6xl4mQ94xuk!-$l8*%+1zQU{)!= zTooUhjC0SNBh!&Ne}Q=1%`_r=Vu1c8RuE!|(g4BQGcd5AbpLbvKv_Z~Y`l!mr!sCc zDBupoc{W@U(6KWqW@xV_`;J0~+WDx|t^WeMri#=q0U5ZN7@@FAv<1!hP6!IYX z>UjbhaEv2Fk<6C0M^@J`lH#LgKJ(`?6z5=uH+ImggSQaZtvh52WTK+EBN~-op#EQKYW`$yBmq z4wgLTJPn3;mtbs0m0RO&+EG>?rb*ZECE0#eeSOFL!2YQ$w}cae>sun`<=}m!=go!v zO2jn<0tNh4E-4)ZA(ixh5nIUuXF-qYl>0I_1)K%EAw`D7~la$=gc@6g{iWF=>i_76?Mc zh#l9h7))<|EY=sK!E|54;c!b;Zp}HLd5*-w^6^whxB98v`*P>cj!Nfu1R%@bcp{cb zUZ24(fUXn3d&oc{6H%u(@4&_O?#HO(qd^YH=V`WJ=u*u6Zie8mE^r_Oz zDw`DaXeq4G#m@EK5+p40Xe!Lr!-jTQLCV3?R1|3#`%45h8#WSA!XoLDMS7=t!SluZ4H56;G z6C9D(B6>k^ur_DGfJ@Y-=3$5HkrI zO+3P>R@$6QZ#ATUI3$)xRBEL#5IKs}yhf&fK;ANA#Qj~G zdE|k|`puh$%dyE4R0$7dZd)M*#e7s%*PKPyrS;d%&S(d{_Ktq^!Hpi&bxZx`?9pEw z%sPjo&adHm95F7Z1{RdY#*a!&LcBZVRe{qhn8d{pOUJ{fOu`_kFg7ZVeRYZ(!ezNktT5{Ab z4BZI$vS0$vm3t9q`ECjDK;pmS{8ZTKs`Js~PYv2|=VkDv{Dtt)cLU@9%K6_KqtqfM zaE*e$f$Xm=;IAURNUXw8g%=?jzG2}10ZA5qXzAaJ@eh)yv5B=ETyVwC-a*CD;GgRJ z4J1~zMUey?4iVlS0zW|F-~0nenLiN3S0)l!T2}D%;<}Z9DzeVgcB+MSj;f$KY;uP%UR#f`0u*@6U@tk@jO3N?Fjq< z{cUUhjrr$rmo>qE?52zKe+>6iP5P_tcUfxsLSy{9*)shB(w`UUveNH`a`kr$VEF@} zKh&|lTD;4;m_H6C&)9#D`kRh;S(NTa=Ve^~xe_0~x$6h8Q@B_qu#ee=(lkI9@F6$0m=z@H=4&h%Q{htM>uHs(Sr@2ry`fgLA zKj8lVXdGPyy)2J%A${}Rm_a{){wHnlM?yGPQ7#KO{8*(_l0QZHuV};nO?c%h?qwSL z3wem|w*2tdxW5&PxC(Wd0QG_w|GPbw|0UFK`u$~U%!`QKcME;=Q@?*erh4_>FP~1n zAldwG9h$$u_$RFK6Uxo20GHqJzc}Rl-EwVz3h4n z;3~%DwD84i>)-8#&#y3k)3BG5cNaP3?t4q}F%yfv?*yEiC>sSo}$f>nh0QNZXH1N)-Q7kbk=2uL9OrF)nXrE@F1y%_8Yn c82=K%QXLKFx%@O{wJjEi6Y56o#$)Bpeg literal 0 HcmV?d00001 diff --git a/mobile-app/gradle/wrapper/gradle-wrapper.properties b/mobile-app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..fd4d1f2b --- /dev/null +++ b/mobile-app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://downloads.gradle.org/distributions/gradle-9.3.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mobile-app/gradlew b/mobile-app/gradlew new file mode 100755 index 00000000..8649100b --- /dev/null +++ b/mobile-app/gradlew @@ -0,0 +1,15 @@ +#!/usr/bin/env sh +set -eu + +APP_HOME=$(cd "$(dirname "$0")" && pwd) + +CLASSPATH="$APP_HOME/gradle/wrapper/gradle-wrapper.jar" + +if [ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ]; then + JAVA_CMD="$JAVA_HOME/bin/java" +else + JAVA_CMD="java" +fi + +exec "$JAVA_CMD" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" + diff --git a/mobile-app/gradlew.bat b/mobile-app/gradlew.bat new file mode 100644 index 00000000..a92ccc3d --- /dev/null +++ b/mobile-app/gradlew.bat @@ -0,0 +1,12 @@ +@echo off +setlocal +set APP_HOME=%~dp0 +set CLASSPATH=%APP_HOME%gradle\wrapper\gradle-wrapper.jar +if not "%JAVA_HOME%"=="" ( + set JAVA_CMD=%JAVA_HOME%\bin\java.exe +) else ( + set JAVA_CMD=java.exe +) +"%JAVA_CMD%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +endlocal + diff --git a/mobile-app/iosApp/README.md b/mobile-app/iosApp/README.md new file mode 100644 index 00000000..a95f8ae4 --- /dev/null +++ b/mobile-app/iosApp/README.md @@ -0,0 +1,20 @@ +# iOS App (SwiftUI) – Setup (Planned) + +Das Repo enthält aktuell den Shared-Code in `mobile-app/shared/` (KMP). +Dieser baut iOS-Frameworks (`TTShared`) für: +- `iosX64` +- `iosArm64` +- `iosSimulatorArm64` + +## Nächste Schritte (wenn wir iOS aktivieren) +1. Xcode: neues SwiftUI App-Projekt anlegen (`iosApp/Trainingstagebuch.xcodeproj`). +2. Shared Framework einbinden: + - Option A (empfohlen): XCFramework Build Task in Gradle + Einbindung als binary target. + - Option B: CocoaPods Integration (KMP cocoapods plugin). +3. iOS Keychain Token Storage implementieren und in Shared injizieren. +4. SwiftUI Screens: + - Login + - Shell (Tabs) + +Hinweis: Die konkrete Einbindung hängt davon ab, ob ihr SPM vs CocoaPods bevorzugt. + diff --git a/mobile-app/scripts/install-debug-emulator.sh b/mobile-app/scripts/install-debug-emulator.sh new file mode 100755 index 00000000..86394655 --- /dev/null +++ b/mobile-app/scripts/install-debug-emulator.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Installiert composeApp im Debug-Modus mit Emulator-tauglicher Backend-URL (Host-PC). +# Nutzung: von mobile-app/ aus: ./scripts/install-debug-emulator.sh +set -euo pipefail +cd "$(dirname "$0")/.." +adb uninstall de.tt_tagebuch.app 2>/dev/null || true +./gradlew :composeApp:clean :composeApp:installDebug \ + -PbackendBaseUrl=http://10.0.2.2:3005 \ + --no-configuration-cache diff --git a/mobile-app/settings.gradle.kts b/mobile-app/settings.gradle.kts index 19e4ae91..0d72a7f2 100644 --- a/mobile-app/settings.gradle.kts +++ b/mobile-app/settings.gradle.kts @@ -6,11 +6,15 @@ pluginManagement { maven("https://maven.pkg.jetbrains.space/public/p/compose/patch") } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} dependencyResolutionManagement { repositories { google() mavenCentral() + maven("https://jitpack.io") maven("https://maven.pkg.jetbrains.space/public/p/compose/patch") } } diff --git a/mobile-app/shared/build.gradle.kts b/mobile-app/shared/build.gradle.kts index fc2ed012..1da29f70 100644 --- a/mobile-app/shared/build.gradle.kts +++ b/mobile-app/shared/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.androidLibrary) @@ -7,11 +9,23 @@ plugins { kotlin { androidTarget { compilations.all { - kotlinOptions { - jvmTarget = "1.8" + compilerOptions.configure { + jvmTarget.set(JvmTarget.JVM_17) } } } + + val iosTargets = listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64(), + ) + iosTargets.forEach { + it.binaries.framework { + baseName = "TTShared" + isStatic = true + } + } sourceSets { commonMain.dependencies { @@ -24,6 +38,10 @@ kotlin { } androidMain.dependencies { implementation(libs.ktor.client.okhttp) + implementation(libs.androidx.security.crypto) + } + iosMain.dependencies { + implementation(libs.ktor.client.darwin) } } } diff --git a/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/api/http/AndroidHttpClientEngineFactory.kt b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/api/http/AndroidHttpClientEngineFactory.kt new file mode 100644 index 00000000..203e4060 --- /dev/null +++ b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/api/http/AndroidHttpClientEngineFactory.kt @@ -0,0 +1,9 @@ +package de.tt_tagebuch.shared.api.http + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.okhttp.OkHttp + +class AndroidHttpClientEngineFactory : HttpClientEngineFactory { + override fun create(): HttpClientEngine = OkHttp.create() +} + diff --git a/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidClubStorage.kt b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidClubStorage.kt new file mode 100644 index 00000000..81535cbb --- /dev/null +++ b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidClubStorage.kt @@ -0,0 +1,38 @@ +package de.tt_tagebuch.shared.state + +import android.content.Context +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class AndroidClubStorage( + context: Context, +) : ClubStorage { + private val prefs = EncryptedSharedPreferences.create( + context, + PREFS_NAME, + MasterKey.Builder(context) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build(), + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, + ) + + override suspend fun loadCurrentClubId(): Int? = withContext(Dispatchers.Default) { + val value = prefs.getString(KEY_CURRENT_CLUB_ID, null) ?: return@withContext null + value.toIntOrNull() + } + + override suspend fun saveCurrentClubId(clubId: Int?) = withContext(Dispatchers.Default) { + val editor = prefs.edit() + if (clubId == null) editor.remove(KEY_CURRENT_CLUB_ID) else editor.putString(KEY_CURRENT_CLUB_ID, clubId.toString()) + editor.apply() + } + + private companion object { + private const val PREFS_NAME = "tttagebuch_club" + private const val KEY_CURRENT_CLUB_ID = "currentClubId" + } +} + diff --git a/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidLanguageStorage.kt b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidLanguageStorage.kt new file mode 100644 index 00000000..b3f9dbf2 --- /dev/null +++ b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidLanguageStorage.kt @@ -0,0 +1,24 @@ +package de.tt_tagebuch.shared.state + +import android.content.Context +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class AndroidLanguageStorage( + context: Context, +) : LanguageStorage { + private val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + + override suspend fun loadLanguageCode(): String? = withContext(Dispatchers.Default) { + prefs.getString(KEY_LANGUAGE_CODE, null) + } + + override suspend fun saveLanguageCode(languageCode: String) = withContext(Dispatchers.Default) { + prefs.edit().putString(KEY_LANGUAGE_CODE, languageCode).apply() + } + + private companion object { + private const val PREFS_NAME = "tttagebuch_language" + private const val KEY_LANGUAGE_CODE = "languageCode" + } +} diff --git a/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidTokenStorage.kt b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidTokenStorage.kt new file mode 100644 index 00000000..2e68f440 --- /dev/null +++ b/mobile-app/shared/src/androidMain/kotlin/de/tt_tagebuch/shared/state/AndroidTokenStorage.kt @@ -0,0 +1,49 @@ +package de.tt_tagebuch.shared.state + +import android.content.Context +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class AndroidTokenStorage( + context: Context, +) : TokenStorage { + + private val prefs = EncryptedSharedPreferences.create( + context, + PREFS_NAME, + MasterKey.Builder(context) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build(), + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, + ) + + override suspend fun load(): AuthTokens? = withContext(Dispatchers.Default) { + val token = prefs.getString(KEY_TOKEN, null) + val username = prefs.getString(KEY_USERNAME, null) + if (token.isNullOrBlank() || username.isNullOrBlank()) null else AuthTokens(token, username) + } + + override suspend fun save(tokens: AuthTokens) = withContext(Dispatchers.Default) { + prefs.edit() + .putString(KEY_TOKEN, tokens.token) + .putString(KEY_USERNAME, tokens.username) + .apply() + } + + override suspend fun clear() = withContext(Dispatchers.Default) { + prefs.edit() + .remove(KEY_TOKEN) + .remove(KEY_USERNAME) + .apply() + } + + private companion object { + private const val PREFS_NAME = "tttagebuch_auth" + private const val KEY_TOKEN = "token" + private const val KEY_USERNAME = "username" + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AccidentApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AccidentApi.kt new file mode 100644 index 00000000..33f3db19 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AccidentApi.kt @@ -0,0 +1,23 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.AccidentReportDto +import de.tt_tagebuch.shared.api.models.CreateAccidentBody +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody + +class AccidentApi( + private val client: AuthedHttpClient, +) { + suspend fun list(clubId: Int, diaryDateId: Int): List { + return client.http.get("/api/accident/$clubId/$diaryDateId").body() + } + + suspend fun create(body: CreateAccidentBody) { + client.http.post("/api/accident") { + setBody(body) + } + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ApiConfig.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ApiConfig.kt new file mode 100644 index 00000000..92630eba --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ApiConfig.kt @@ -0,0 +1,18 @@ +package de.tt_tagebuch.shared.api + +data class ApiConfig( + val baseUrl: String, +) + +fun ApiConfig.toAbsoluteUrl(relativeOrAbsolute: String): String { + val t = relativeOrAbsolute.trim() + if (t.startsWith("http://", ignoreCase = true) || t.startsWith("https://", ignoreCase = true)) { + return t + } + val base = baseUrl.trimEnd('/') + val path = if (t.startsWith("/")) t else "/$t" + return base + path +} + +fun memberProfileImagePath(clubId: Int, memberId: Int) = "/api/clubmembers/image/$clubId/$memberId" + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AuthApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AuthApi.kt new file mode 100644 index 00000000..3711d7af --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/AuthApi.kt @@ -0,0 +1,23 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.LoginRequest +import de.tt_tagebuch.shared.api.models.LoginResponse +import io.ktor.client.call.body +import io.ktor.client.request.post +import io.ktor.client.request.setBody + +class AuthApi( + private val client: AuthedHttpClient, +) { + suspend fun login(email: String, password: String): LoginResponse { + return client.http.post("/api/auth/login") { + setBody(LoginRequest(email = email, password = password)) + }.body() + } + + suspend fun logout() { + client.http.post("/api/auth/logout") + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ClubsApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ClubsApi.kt new file mode 100644 index 00000000..36299614 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ClubsApi.kt @@ -0,0 +1,35 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.Club +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import kotlinx.serialization.Serializable + +@Serializable +private data class CreateClubBody(val name: String) + +class ClubsApi( + private val client: AuthedHttpClient, +) { + suspend fun listClubs(): List { + return client.http.get("/api/clubs").body() + } + + suspend fun createClub(name: String): Club { + return client.http.post("/api/clubs") { + setBody(CreateClubBody(name.trim())) + }.body() + } + + suspend fun getClub(clubId: Int): Club { + return client.http.get("/api/clubs/$clubId").body() + } + + suspend fun requestAccess(clubId: Int) { + client.http.get("/api/clubs/request/$clubId") + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryApi.kt new file mode 100644 index 00000000..8bc22f21 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryApi.kt @@ -0,0 +1,133 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.AddDiaryNoteRequest +import de.tt_tagebuch.shared.api.models.AddFreeformActivityBody +import de.tt_tagebuch.shared.api.models.AddDiaryPlanGroupActivityRequest +import de.tt_tagebuch.shared.api.models.CreateDiaryDateRequest +import de.tt_tagebuch.shared.api.models.CreateDiaryPlanActivityRequest +import de.tt_tagebuch.shared.api.models.DiaryDate +import de.tt_tagebuch.shared.api.models.DiaryDateActivityItem +import de.tt_tagebuch.shared.api.models.DiaryFreeformActivity +import de.tt_tagebuch.shared.api.models.DiaryNote +import de.tt_tagebuch.shared.api.models.DiaryTag +import de.tt_tagebuch.shared.api.models.LinkDiaryTagRequest +import de.tt_tagebuch.shared.api.models.UpdateDiaryPlanActivityOrderRequest +import de.tt_tagebuch.shared.api.models.UpdateDiaryPlanActivityRequest +import de.tt_tagebuch.shared.api.models.UpdateDiaryTimesRequest +import de.tt_tagebuch.shared.api.models.UpdateNestedPlanGroupActivityRequest +import io.ktor.client.call.body +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.client.request.post +import io.ktor.client.request.put +import io.ktor.client.request.setBody + +class DiaryApi( + private val client: AuthedHttpClient, +) { + suspend fun listDates(clubId: Int): List { + return client.http.get("/api/diary/$clubId").body() + } + + suspend fun listFreeformActivities(diaryDateId: Int): List { + return client.http.get("/api/activities/$diaryDateId").body() + } + + suspend fun addFreeformActivity(diaryDateId: Int, description: String): DiaryFreeformActivity { + return client.http.post("/api/activities/add") { + setBody(AddFreeformActivityBody(diaryDateId = diaryDateId, description = description.trim())) + }.body() + } + + suspend fun listDateActivities(clubId: Int, diaryDateId: Int): List { + return client.http.get("/api/diary-date-activities/$clubId/$diaryDateId").body() + } + + suspend fun createDateActivity(clubId: Int, body: CreateDiaryPlanActivityRequest) { + client.http.post("/api/diary-date-activities/$clubId") { + setBody(body) + } + } + + suspend fun addDateGroupActivity(body: AddDiaryPlanGroupActivityRequest) { + client.http.post("/api/diary-date-activities/group") { + setBody(body) + } + } + + suspend fun updateDateActivity(clubId: Int, activityId: Int, body: UpdateDiaryPlanActivityRequest) { + client.http.put("/api/diary-date-activities/$clubId/$activityId") { + setBody(body) + } + } + + suspend fun updateDateActivityOrder(clubId: Int, activityId: Int, orderId: Int) { + client.http.put("/api/diary-date-activities/$clubId/$activityId/order") { + setBody(UpdateDiaryPlanActivityOrderRequest(orderId)) + } + } + + suspend fun deleteDateActivity(clubId: Int, activityId: Int) { + client.http.delete("/api/diary-date-activities/$clubId/$activityId") + } + + suspend fun updateNestedGroupActivity(clubId: Int, groupActivityId: Int, body: UpdateNestedPlanGroupActivityRequest) { + client.http.put("/api/diary-date-activities/group/$clubId/$groupActivityId") { + setBody(body) + } + } + + suspend fun deleteNestedGroupActivity(clubId: Int, groupActivityId: Int) { + client.http.delete("/api/diary-date-activities/group/$clubId/$groupActivityId") + } + + suspend fun createDate(clubId: Int, date: String, trainingStart: String?, trainingEnd: String?): DiaryDate { + return client.http.post("/api/diary/$clubId") { + setBody(CreateDiaryDateRequest(date, trainingStart, trainingEnd)) + }.body() + } + + suspend fun updateTimes(clubId: Int, dateId: Int, trainingStart: String?, trainingEnd: String?): DiaryDate { + return client.http.put("/api/diary/$clubId") { + setBody(UpdateDiaryTimesRequest(dateId, trainingStart, trainingEnd)) + }.body() + } + + suspend fun deleteDate(clubId: Int, dateId: Int) { + client.http.delete("/api/diary/$clubId/$dateId") + } + + suspend fun addNote(diaryDateId: Int, content: String): List { + return client.http.post("/api/diary/note") { + setBody(AddDiaryNoteRequest(diaryDateId, content)) + }.body() + } + + suspend fun deleteNote(noteId: Int): List { + return client.http.delete("/api/diary/note/$noteId").body() + } + + suspend fun createTag(name: String): DiaryTag { + return client.http.post("/api/tags") { + setBody(mapOf("name" to name)) + }.body() + } + + suspend fun listTags(): List { + return client.http.get("/api/tags").body() + } + + suspend fun linkTag(clubId: Int, diaryDateId: Int, tagId: Int): List { + return client.http.post("/api/diary/tag/$clubId/add-tag") { + setBody(LinkDiaryTagRequest(diaryDateId, tagId)) + }.body() + } + + suspend fun removeTag(clubId: Int, tagId: Int) { + client.http.delete("/api/diary/$clubId/tag") { + parameter("tagId", tagId) + } + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberActivitiesApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberActivitiesApi.kt new file mode 100644 index 00000000..4ea3dd01 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberActivitiesApi.kt @@ -0,0 +1,28 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.AddMemberActivityParticipantsBody +import de.tt_tagebuch.shared.api.models.DiaryMemberActivityLink +import io.ktor.client.call.body +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody + +class DiaryMemberActivitiesApi( + private val client: AuthedHttpClient, +) { + suspend fun list(clubId: Int, diaryDateOrGroupActivityId: Int): List { + return client.http.get("/api/diary-member-activities/$clubId/$diaryDateOrGroupActivityId").body() + } + + suspend fun add(clubId: Int, diaryDateOrGroupActivityId: Int, participantIds: List) { + client.http.post("/api/diary-member-activities/$clubId/$diaryDateOrGroupActivityId") { + setBody(AddMemberActivityParticipantsBody(participantIds = participantIds)) + } + } + + suspend fun remove(clubId: Int, diaryDateOrGroupActivityId: Int, participantRowId: Int) { + client.http.delete("/api/diary-member-activities/$clubId/$diaryDateOrGroupActivityId/$participantRowId") + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberApi.kt new file mode 100644 index 00000000..2a2d5a72 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/DiaryMemberApi.kt @@ -0,0 +1,56 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.AddDiaryMemberNoteBody +import de.tt_tagebuch.shared.api.models.DiaryMemberNoteDto +import de.tt_tagebuch.shared.api.models.DiaryMemberTagLinkDto +import de.tt_tagebuch.shared.api.models.DiaryMemberTagMutationBody +import io.ktor.client.call.body +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.client.request.post +import io.ktor.client.request.setBody + +class DiaryMemberApi( + private val client: AuthedHttpClient, +) { + suspend fun listNotes(clubId: Int, diaryDateId: Int, memberId: Int): List { + return client.http.get("/api/diarymember/$clubId/note") { + parameter("diaryDateId", diaryDateId) + parameter("memberId", memberId) + }.body() + } + + suspend fun addNote(clubId: Int, body: AddDiaryMemberNoteBody): List { + return client.http.post("/api/diarymember/$clubId/note") { + setBody(body) + }.body() + } + + suspend fun deleteNote(clubId: Int, noteId: Int, diaryDateId: Int, memberId: Int): List { + return client.http.delete("/api/diarymember/$clubId/note/$noteId") { + parameter("diaryDateId", diaryDateId) + parameter("memberId", memberId) + }.body() + } + + suspend fun listTags(clubId: Int, diaryDateId: Int, memberId: Int): List { + return client.http.get("/api/diarymember/$clubId/tag") { + parameter("diaryDateId", diaryDateId) + parameter("memberId", memberId) + }.body() + } + + suspend fun addTag(clubId: Int, body: DiaryMemberTagMutationBody): List { + return client.http.post("/api/diarymember/$clubId/tag") { + setBody(body) + }.body() + } + + suspend fun removeTag(clubId: Int, body: DiaryMemberTagMutationBody): List { + return client.http.post("/api/diarymember/$clubId/tag/remove") { + setBody(body) + }.body() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/GroupApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/GroupApi.kt new file mode 100644 index 00000000..046ba852 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/GroupApi.kt @@ -0,0 +1,42 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.CreateTrainingGroupBody +import de.tt_tagebuch.shared.api.models.DeleteTrainingGroupBody +import de.tt_tagebuch.shared.api.models.DiaryPlanGroup +import de.tt_tagebuch.shared.api.models.UpdateTrainingGroupBody +import io.ktor.client.call.body +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.put +import io.ktor.client.request.setBody + +/** + * Trainingsgruppen am Tagebuch-Tag (`/api/group`), für gruppenbezogene Plan-Einträge. + */ +class GroupApi( + private val client: AuthedHttpClient, +) { + suspend fun listForDiaryDate(clubId: Int, diaryDateId: Int): List { + return client.http.get("/api/group/$clubId/$diaryDateId").body() + } + + suspend fun create(body: CreateTrainingGroupBody): DiaryPlanGroup { + return client.http.post("/api/group") { + setBody(body) + }.body() + } + + suspend fun update(groupId: Int, body: UpdateTrainingGroupBody): DiaryPlanGroup { + return client.http.put("/api/group/$groupId") { + setBody(body) + }.body() + } + + suspend fun delete(groupId: Int, body: DeleteTrainingGroupBody) { + client.http.delete("/api/group/$groupId") { + setBody(body) + } + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberActivitiesApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberActivitiesApi.kt new file mode 100644 index 00000000..26905f19 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberActivitiesApi.kt @@ -0,0 +1,24 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.MemberActivityStatDto +import de.tt_tagebuch.shared.api.models.MemberLastParticipationDto +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.parameter + +class MemberActivitiesApi( + private val client: AuthedHttpClient, +) { + suspend fun listActivityStats(clubId: Int, memberId: Int, period: String = "year"): List { + return client.http.get("/api/member-activities/$clubId/$memberId") { + parameter("period", period) + }.body() + } + + suspend fun listLastParticipations(clubId: Int, memberId: Int, limit: Int = 12): List { + return client.http.get("/api/member-activities/$clubId/$memberId/last-participations") { + parameter("limit", limit) + }.body() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberGroupPhotosApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberGroupPhotosApi.kt new file mode 100644 index 00000000..f185b4b7 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MemberGroupPhotosApi.kt @@ -0,0 +1,51 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.MemberGroupPhotoDto +import de.tt_tagebuch.shared.api.models.MemberGroupPhotoListResponse +import io.ktor.client.call.body +import io.ktor.client.request.delete +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.forms.formData +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.ContentType +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import io.ktor.http.contentType + +class MemberGroupPhotosApi( + private val client: AuthedHttpClient, +) { + suspend fun list(clubId: Int): List { + val res: MemberGroupPhotoListResponse = client.http.get("/api/member-group-photos/$clubId").body() + return res.photos + } + + suspend fun upload(clubId: Int, imageBytes: ByteArray, title: String, description: String) { + client.http.post("/api/member-group-photos/$clubId") { + contentType(ContentType.MultiPart.FormData) + setBody( + MultiPartFormDataContent( + formData { + append("title", title.ifBlank { "Gruppenfoto" }) + append("description", description) + append( + "image", + imageBytes, + Headers.build { + append(HttpHeaders.ContentType, "image/jpeg") + append(HttpHeaders.ContentDisposition, "filename=\"group.jpg\"") + }, + ) + }, + ), + ) + } + } + + suspend fun delete(clubId: Int, photoId: Int) { + client.http.delete("/api/member-group-photos/$clubId/$photoId") + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MembersApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MembersApi.kt new file mode 100644 index 00000000..5da5c1b0 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/MembersApi.kt @@ -0,0 +1,54 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.Member +import de.tt_tagebuch.shared.api.models.MemberSetBody +import io.ktor.client.call.body +import io.ktor.client.request.forms.formData +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.ContentType +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import io.ktor.http.contentType +import io.ktor.client.request.forms.MultiPartFormDataContent + +class MembersApi( + private val client: AuthedHttpClient, +) { + suspend fun listMembers(clubId: Int, showAll: Boolean = true): List { + return client.http.get("/api/clubmembers/get/$clubId/$showAll").body() + } + + suspend fun setMember(clubId: Int, body: MemberSetBody) { + client.http.post("/api/clubmembers/set/$clubId") { + setBody(body) + } + } + + suspend fun uploadMemberImage(clubId: Int, memberId: Int, imageBytes: ByteArray, makePrimary: Boolean = true) { + client.http.post("/api/clubmembers/image/$clubId/$memberId") { + if (makePrimary) { + parameter("makePrimary", "true") + } + contentType(ContentType.MultiPart.FormData) + setBody( + MultiPartFormDataContent( + formData { + append( + "image", + imageBytes, + Headers.build { + append(HttpHeaders.ContentType, "image/jpeg") + append(HttpHeaders.ContentDisposition, "filename=\"member.jpg\"") + }, + ) + }, + ), + ) + } + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ParticipantsApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ParticipantsApi.kt new file mode 100644 index 00000000..b3d7db90 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/ParticipantsApi.kt @@ -0,0 +1,44 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.DiaryTrainingParticipant +import de.tt_tagebuch.shared.api.models.ParticipantGroupRequest +import de.tt_tagebuch.shared.api.models.ParticipantMutationRequest +import io.ktor.client.call.body +import de.tt_tagebuch.shared.api.models.ParticipantStatusRequest +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.put +import io.ktor.client.request.setBody + +class ParticipantsApi( + private val client: AuthedHttpClient, +) { + suspend fun listForDate(diaryDateId: Int): List { + return client.http.get("/api/participants/$diaryDateId").body() + } + + suspend fun add(diaryDateId: Int, memberId: Int): DiaryTrainingParticipant { + return client.http.post("/api/participants/add") { + setBody(ParticipantMutationRequest(diaryDateId, memberId)) + }.body() + } + + suspend fun remove(diaryDateId: Int, memberId: Int) { + client.http.post("/api/participants/remove") { + setBody(ParticipantMutationRequest(diaryDateId, memberId)) + } + } + + suspend fun updateAttendanceStatus(diaryDateId: Int, memberId: Int, attendanceStatus: String) { + client.http.put("/api/participants/$diaryDateId/$memberId/status") { + setBody(ParticipantStatusRequest(attendanceStatus = attendanceStatus)) + } + } + + suspend fun updateParticipantGroup(diaryDateId: Int, memberId: Int, groupId: Int?) { + client.http.put("/api/participants/$diaryDateId/$memberId/group") { + setBody(ParticipantGroupRequest(groupId = groupId)) + } + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PermissionsApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PermissionsApi.kt new file mode 100644 index 00000000..519a4db1 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PermissionsApi.kt @@ -0,0 +1,15 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.UserClubPermissions +import io.ktor.client.call.body +import io.ktor.client.request.get + +class PermissionsApi( + private val client: AuthedHttpClient, +) { + suspend fun getUserPermissions(clubId: Int): UserClubPermissions { + return client.http.get("/api/permissions/$clubId").body() + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PredefinedActivitiesApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PredefinedActivitiesApi.kt new file mode 100644 index 00000000..d33549ec --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PredefinedActivitiesApi.kt @@ -0,0 +1,28 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.PredefinedActivityDto +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.parameter + +class PredefinedActivitiesApi( + private val client: AuthedHttpClient, +) { + suspend fun list(scope: String? = null): List { + return client.http.get("/api/predefined-activities") { + if (scope != null) parameter("scope", scope) + }.body() + } + + suspend fun search(query: String, limit: Int = 20): List { + return client.http.get("/api/predefined-activities/search/query") { + parameter("q", query) + parameter("limit", limit) + }.body() + } + + suspend fun getById(id: Int): PredefinedActivityDto { + return client.http.get("/api/predefined-activities/$id").body() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PublicAuthApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PublicAuthApi.kt new file mode 100644 index 00000000..0fe77243 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/PublicAuthApi.kt @@ -0,0 +1,37 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.PublicHttpClient +import de.tt_tagebuch.shared.api.models.ForgotPasswordRequest +import de.tt_tagebuch.shared.api.models.MessageResponse +import de.tt_tagebuch.shared.api.models.RegisterRequest +import de.tt_tagebuch.shared.api.models.ResetPasswordRequest +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody + +class PublicAuthApi( + private val client: PublicHttpClient, +) { + suspend fun register(email: String, password: String) { + client.http.post("/api/auth/register") { + setBody(RegisterRequest(email = email, password = password)) + } + } + + suspend fun activate(activationCode: String) { + client.http.get("/api/auth/activate/$activationCode") + } + + suspend fun forgotPassword(email: String): MessageResponse { + return client.http.post("/api/auth/forgot-password") { + setBody(ForgotPasswordRequest(email = email)) + }.body() + } + + suspend fun resetPassword(token: String, password: String): MessageResponse { + return client.http.post("/api/auth/reset-password") { + setBody(ResetPasswordRequest(token = token, password = password)) + }.body() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/SessionApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/SessionApi.kt new file mode 100644 index 00000000..89c64003 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/SessionApi.kt @@ -0,0 +1,15 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.SessionStatusResponse +import io.ktor.client.call.body +import io.ktor.client.request.get + +class SessionApi( + private val client: AuthedHttpClient, +) { + suspend fun status(): SessionStatusResponse { + return client.http.get("/api/session/status").body() + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingGroupsApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingGroupsApi.kt new file mode 100644 index 00000000..9ec79ea5 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingGroupsApi.kt @@ -0,0 +1,34 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.TrainingGroupDto +import io.ktor.client.call.body +import io.ktor.client.request.delete +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.ContentType +import io.ktor.http.contentType + +class TrainingGroupsApi( + private val client: AuthedHttpClient, +) { + suspend fun listGroups(clubId: Int): List { + return client.http.get("/api/training-groups/$clubId").body() + } + + suspend fun listMemberGroups(clubId: Int, memberId: Int): List { + return client.http.get("/api/training-groups/$clubId/member/$memberId").body() + } + + suspend fun addMemberToGroup(clubId: Int, groupId: Int, memberId: Int) { + client.http.post("/api/training-groups/$clubId/$groupId/member/$memberId") { + contentType(ContentType.Application.Json) + setBody("{}") + } + } + + suspend fun removeMemberFromGroup(clubId: Int, groupId: Int, memberId: Int) { + client.http.delete("/api/training-groups/$clubId/$groupId/member/$memberId") + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingStatsApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingStatsApi.kt new file mode 100644 index 00000000..32883341 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingStatsApi.kt @@ -0,0 +1,15 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.TrainingStats +import io.ktor.client.call.body +import io.ktor.client.request.get + +class TrainingStatsApi( + private val client: AuthedHttpClient, +) { + suspend fun getStats(clubId: Int): TrainingStats { + return client.http.get("/api/training-stats/$clubId").body() + } +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingTimesApi.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingTimesApi.kt new file mode 100644 index 00000000..000b66f4 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/TrainingTimesApi.kt @@ -0,0 +1,14 @@ +package de.tt_tagebuch.shared.api + +import de.tt_tagebuch.shared.api.http.AuthedHttpClient +import de.tt_tagebuch.shared.api.models.TrainingGroupDto +import io.ktor.client.call.body +import io.ktor.client.request.get + +class TrainingTimesApi( + private val client: AuthedHttpClient, +) { + suspend fun listGroupsWithTimes(clubId: Int): List { + return client.http.get("/api/training-times/$clubId").body() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiErrorMessage.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiErrorMessage.kt new file mode 100644 index 00000000..92de3c25 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiErrorMessage.kt @@ -0,0 +1,35 @@ +package de.tt_tagebuch.shared.api.http + +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsText +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonPrimitive + +private val looseJson = Json { ignoreUnknownKeys = true } + +/** + * Liest JSON-Fehlerantworten (`error`, `message`) wie die Node-API sie oft liefert. + */ +internal suspend fun HttpResponse.userFacingErrorOr(fallback: String): String { + val raw = runCatching { bodyAsText() }.getOrNull().orEmpty().trim() + if (raw.isEmpty()) return fallback + val extracted = runCatching { + val el = looseJson.parseToJsonElement(raw) + if (el !is JsonObject) return@runCatching null + el["error"]?.jsonPrimitive?.content?.takeIf { it.isNotBlank() } + ?: el["message"]?.jsonPrimitive?.content?.takeIf { it.isNotBlank() } + }.getOrNull() + val text = extracted ?: raw.take(500).let { if (raw.length > 500) "$it…" else it } + return mapKnownBackendErrorTokens(text) +} + +private fun mapKnownBackendErrorTokens(raw: String): String { + return when (raw.trim().lowercase()) { + "alreadyexists" -> "Ein Verein mit diesem Namen existiert bereits." + "noaccess" -> "Kein Zugriff auf diese Ressource." + "internalerror" -> "Serverfehler. Bitte später erneut versuchen." + "notrequested" -> "Für diesen Verein wurde kein Zugriff beantragt." + else -> raw + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiException.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiException.kt new file mode 100644 index 00000000..f1201652 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/ApiException.kt @@ -0,0 +1,7 @@ +package de.tt_tagebuch.shared.api.http + +class ApiException( + val statusCode: Int, + message: String, +) : RuntimeException(message) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/AuthedHttpClient.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/AuthedHttpClient.kt new file mode 100644 index 00000000..e20775a4 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/AuthedHttpClient.kt @@ -0,0 +1,61 @@ +package de.tt_tagebuch.shared.api.http + +import de.tt_tagebuch.shared.api.ApiConfig +import de.tt_tagebuch.shared.state.TokenProvider +import io.ktor.client.HttpClient +import io.ktor.client.plugins.DefaultRequest +import io.ktor.client.plugins.HttpResponseValidator +import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.header +import io.ktor.client.request.url +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.json.Json + +class AuthedHttpClient( + apiConfig: ApiConfig, + private val tokenProvider: TokenProvider, + httpClientEngineFactory: HttpClientEngineFactory, + private val onUnauthorized: () -> Unit = {}, +) { + val http: HttpClient = HttpClient(httpClientEngineFactory.create()) { + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + } + ) + } + install(HttpTimeout) { + requestTimeoutMillis = 60_000 + connectTimeoutMillis = 15_000 + socketTimeoutMillis = 60_000 + } + HttpResponseValidator { + validateResponse { response -> + val statusCode = response.status.value + if (statusCode == 401) { + onUnauthorized() + val detail = response.userFacingErrorOr("Session abgelaufen") + throw ApiException(statusCode, detail) + } + if (statusCode >= 400) { + val detail = response.userFacingErrorOr("API Fehler $statusCode") + throw ApiException(statusCode, detail) + } + } + } + install(DefaultRequest) { + url(apiConfig.baseUrl) + contentType(ContentType.Application.Json) + tokenProvider.token?.let { token -> + header("authcode", token) + } + tokenProvider.username?.let { username -> + header("userid", username) + } + } + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/HttpClientEngineFactory.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/HttpClientEngineFactory.kt new file mode 100644 index 00000000..173ea2a8 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/HttpClientEngineFactory.kt @@ -0,0 +1,8 @@ +package de.tt_tagebuch.shared.api.http + +import io.ktor.client.engine.HttpClientEngine + +interface HttpClientEngineFactory { + fun create(): HttpClientEngine +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/PublicHttpClient.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/PublicHttpClient.kt new file mode 100644 index 00000000..440a405d --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/http/PublicHttpClient.kt @@ -0,0 +1,50 @@ +package de.tt_tagebuch.shared.api.http + +import de.tt_tagebuch.shared.api.ApiConfig +import io.ktor.client.HttpClient +import io.ktor.client.plugins.DefaultRequest +import io.ktor.client.plugins.HttpResponseValidator +import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.url +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.json.Json + +/** + * HTTP-Client ohne Auth-Header – für Login, Registrierung, Passwort-Reset. + * 401 löst **keinen** globalen Logout aus (nur [ApiException]). + */ +class PublicHttpClient( + apiConfig: ApiConfig, + httpClientEngineFactory: HttpClientEngineFactory, +) { + val http: HttpClient = HttpClient(httpClientEngineFactory.create()) { + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + }, + ) + } + install(HttpTimeout) { + requestTimeoutMillis = 60_000 + connectTimeoutMillis = 15_000 + socketTimeoutMillis = 60_000 + } + HttpResponseValidator { + validateResponse { response -> + val statusCode = response.status.value + if (statusCode >= 400) { + val detail = response.userFacingErrorOr("API Fehler $statusCode") + throw ApiException(statusCode, detail) + } + } + } + install(DefaultRequest) { + url(apiConfig.baseUrl) + contentType(ContentType.Application.Json) + } + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/AccidentDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/AccidentDtos.kt new file mode 100644 index 00000000..dc6653c7 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/AccidentDtos.kt @@ -0,0 +1,23 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class AccidentReportDto( + val accident: String, + val firstName: String? = null, + val lastName: String? = null, +) + +fun AccidentReportDto.memberLabel(): String { + val parts = listOfNotNull(firstName?.trim()?.takeIf { it.isNotEmpty() }, lastName?.trim()?.takeIf { it.isNotEmpty() }) + return if (parts.isEmpty()) "—" else parts.joinToString(" ") +} + +@Serializable +data class CreateAccidentBody( + val clubId: Int, + val memberId: Int, + val diaryDateId: Int, + val accident: String, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Club.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Club.kt new file mode 100644 index 00000000..d1ffde8a --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Club.kt @@ -0,0 +1,14 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class Club( + val id: Int, + val name: String, + val greetingText: String? = null, + val associationMemberNumber: String? = null, + val myTischtennisFedNickname: String? = null, + val autoFetchRankings: Boolean? = null, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ClubPermissionHelpers.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ClubPermissionHelpers.kt new file mode 100644 index 00000000..bd71e7af --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ClubPermissionHelpers.kt @@ -0,0 +1,32 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.booleanOrNull +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive + +private fun JsonObject.boolAt(module: String, key: String): Boolean { + val mod = this[module]?.jsonObject ?: return false + return mod[key]?.jsonPrimitive?.booleanOrNull == true +} + +/** Vereinsbesitzer haben volle Rechte. */ +fun UserClubPermissions.canReadDiary(): Boolean { + if (isOwner) return true + return permissions.boolAt("diary", "read") +} + +fun UserClubPermissions.canWriteDiary(): Boolean { + if (isOwner) return true + return permissions.boolAt("diary", "write") +} + +fun UserClubPermissions.canReadMembers(): Boolean { + if (isOwner) return true + return permissions.boolAt("members", "read") +} + +fun UserClubPermissions.canWriteMembers(): Boolean { + if (isOwner) return true + return permissions.boolAt("members", "write") +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryActivityExtras.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryActivityExtras.kt new file mode 100644 index 00000000..ccdd7ae3 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryActivityExtras.kt @@ -0,0 +1,30 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +/** Freitext-Aktivitäten zum Tag (`/api/activities`), nicht der Trainingsplan. */ +@Serializable +data class DiaryFreeformActivity( + val id: Int, + val description: String, + val diaryDateId: Int, +) + +@Serializable +data class AddFreeformActivityBody( + val diaryDateId: Int, + val description: String, +) + +/** Verknüpfung Teilnehmer-Zeile ↔ Plan-/Gruppen-Aktivität (`/api/diary-member-activities`). */ +@Serializable +data class DiaryMemberActivityLink( + val id: Int, + val diaryDateActivityId: Int, + val participantId: Int, +) + +@Serializable +data class AddMemberActivityParticipantsBody( + val participantIds: List, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDate.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDate.kt new file mode 100644 index 00000000..a9f030b6 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDate.kt @@ -0,0 +1,53 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class DiaryDate( + val id: Int, + val clubId: Int, + val date: String, + val trainingStart: String? = null, + val trainingEnd: String? = null, + val diaryNotes: List = emptyList(), + val diaryTags: List = emptyList(), +) + +@Serializable +data class DiaryNote( + val id: Int, + val content: String? = null, + val createdAt: String? = null, +) + +@Serializable +data class DiaryTag( + val id: Int, + val name: String, +) + +@Serializable +data class CreateDiaryDateRequest( + val date: String, + val trainingStart: String? = null, + val trainingEnd: String? = null, +) + +@Serializable +data class UpdateDiaryTimesRequest( + val dateId: Int, + val trainingStart: String? = null, + val trainingEnd: String? = null, +) + +@Serializable +data class LinkDiaryTagRequest( + val diaryDateId: Int, + val tagId: Int, +) + +@Serializable +data class AddDiaryNoteRequest( + val diaryDateId: Int, + val content: String, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDateActivityItem.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDateActivityItem.kt new file mode 100644 index 00000000..9ee5a75f --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryDateActivityItem.kt @@ -0,0 +1,55 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class DiaryDateActivityItem( + val id: Int, + val orderId: Int = 0, + val isTimeblock: Boolean = false, + val duration: Int? = null, + val durationText: String? = null, + /** Trainingsgruppe (Tabellen `Group`) für getrennte Pläne am selben Tag. */ + val groupId: Int? = null, + val planGroup: DiaryPlanGroupSummary? = null, + val predefinedActivity: PredefinedActivitySummary? = null, + val groupActivities: List = emptyList(), +) + +@Serializable +data class DiaryPlanGroupSummary( + val id: Int? = null, + val name: String? = null, +) + +@Serializable +data class PredefinedActivitySummary( + val id: Int? = null, + val name: String? = null, + val code: String? = null, + /** Wie Backend: z. B. `/api/predefined-activities/…/image/…` */ + val imageLink: String? = null, + val imageUrl: String? = null, +) + +@Serializable +data class GroupActivitySummary( + val id: Int? = null, + val orderId: Int? = null, + val groupPredefinedActivity: PredefinedActivitySummary? = null, +) + +fun PredefinedActivitySummary?.displayLabel(): String { + if (this == null) return "" + val n = name?.trim().orEmpty() + if (n.isNotEmpty()) return n + val c = code?.trim().orEmpty() + if (c.isNotEmpty()) return c + return "" +} + +fun DiaryDateActivityItem.displayTitle(fallbackTimeblock: String): String { + val label = predefinedActivity.displayLabel() + if (label.isNotEmpty()) return label + return if (isTimeblock) fallbackTimeblock else "" +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryImagePaths.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryImagePaths.kt new file mode 100644 index 00000000..7e12fc05 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryImagePaths.kt @@ -0,0 +1,23 @@ +package de.tt_tagebuch.shared.api.models + +private val imageIdInPath = Regex("/image/(\\d+)") + +fun PredefinedActivitySummary?.hasDisplayableImage(): Boolean { + if (this == null) return false + val link = imageLink?.trim().orEmpty() + if (link.isEmpty()) return false + return imageIdInPath.containsMatchIn(link) +} + +/** Relativer API-Pfad (beginnt mit `/`) oder `null`. */ +fun PredefinedActivitySummary?.imageRelativePath(): String? { + if (!hasDisplayableImage()) return null + val link = this!!.imageLink!!.trim() + return if (link.startsWith("/")) link else "/$link" +} + +fun DiaryDateActivityItem.mainActivityImagePath(): String? = + predefinedActivity.imageRelativePath() + +fun GroupActivitySummary.nestedActivityImagePath(): String? = + groupPredefinedActivity.imageRelativePath() diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryMemberDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryMemberDtos.kt new file mode 100644 index 00000000..30343a56 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryMemberDtos.kt @@ -0,0 +1,51 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class DiaryMemberNoteDto( + val id: Int, + val memberId: Int? = null, + val diaryDateId: Int? = null, + val content: String? = null, + val createdAt: String? = null, +) + +@Serializable +data class DiaryTagNested( + val id: Int, + val name: String? = null, + val label: String? = null, +) + +@Serializable +data class DiaryMemberTagLinkDto( + val id: Int, + val memberId: Int? = null, + val diaryDateId: Int? = null, + val tagId: Int? = null, + val tag: DiaryTagNested? = null, +) + +fun DiaryMemberTagLinkDto.tagDefinitionId(): Int = tag?.id ?: tagId ?: 0 + +fun DiaryMemberTagLinkDto.tagDisplayName(): String { + val fromNested = tag?.label?.takeIf { it.isNotBlank() } ?: tag?.name?.takeIf { it.isNotBlank() } + if (!fromNested.isNullOrBlank()) return fromNested + val tid = tagDefinitionId() + return if (tid > 0) "Tag $tid" else "Tag" +} + +@Serializable +data class AddDiaryMemberNoteBody( + val memberId: Int, + val diaryDateId: Int, + val content: String, +) + +@Serializable +data class DiaryMemberTagMutationBody( + val diaryDateId: Int, + val memberId: Int, + val tagId: Int, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryPlanDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryPlanDtos.kt new file mode 100644 index 00000000..5714314e --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryPlanDtos.kt @@ -0,0 +1,80 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class DiaryPlanGroup( + val id: Int, + val name: String? = null, + val lead: String? = null, + val diaryDateId: Int? = null, +) + +@Serializable +data class CreateDiaryPlanActivityRequest( + val diaryDateId: Int, + val activity: String = "", + val predefinedActivityId: Int? = null, + val duration: Int? = null, + val durationText: String? = null, + val isTimeblock: Boolean = false, + val groupId: Int? = null, +) + +@Serializable +data class AddDiaryPlanGroupActivityRequest( + val clubId: Int, + val diaryDateId: Int, + val groupId: Int, + val activity: String, + val predefinedActivityId: Int? = null, + val timeblockId: Int? = null, + val duration: Int? = null, + val durationText: String? = null, +) + +@Serializable +data class UpdateDiaryPlanActivityRequest( + val predefinedActivityId: Int? = null, + val customActivityName: String? = null, + val duration: Int? = null, + val durationText: String? = null, + val orderId: Int? = null, + val groupId: Int? = null, +) + +@Serializable +data class UpdateDiaryPlanActivityOrderRequest( + val orderId: Int, +) + +@Serializable +data class UpdateNestedPlanGroupActivityRequest( + val predefinedActivityId: Int? = null, + val duration: Int? = null, + val durationText: String? = null, + val orderId: Int? = null, + val groupId: Int? = null, +) + +@Serializable +data class CreateTrainingGroupBody( + val clubid: Int, + val dateid: Int, + val name: String, + val lead: String? = null, +) + +@Serializable +data class UpdateTrainingGroupBody( + val clubid: Int, + val dateid: Int, + val name: String, + val lead: String? = null, +) + +@Serializable +data class DeleteTrainingGroupBody( + val clubid: Int, + val dateid: Int, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryTrainingParticipant.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryTrainingParticipant.kt new file mode 100644 index 00000000..3e8c4d69 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/DiaryTrainingParticipant.kt @@ -0,0 +1,30 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class DiaryTrainingParticipant( + val id: Int, + val diaryDateId: Int, + val memberId: Int, + val attendanceStatus: String? = null, + val groupId: Int? = null, + val notes: String? = null, +) + +@Serializable +data class ParticipantMutationRequest( + val diaryDateId: Int, + val memberId: Int, +) + +@Serializable +data class ParticipantGroupRequest( + val groupId: Int? = null, +) + +/** Wie im Web: nur diese gelten als „nimmt teil“ für die Auswahl. */ +fun DiaryTrainingParticipant.isPresentParticipant(): Boolean { + val s = attendanceStatus + return s.isNullOrBlank() || s == "present" +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginRequest.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginRequest.kt new file mode 100644 index 00000000..a79d4943 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginRequest.kt @@ -0,0 +1,10 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class LoginRequest( + val email: String, + val password: String, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginResponse.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginResponse.kt new file mode 100644 index 00000000..b06ad700 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/LoginResponse.kt @@ -0,0 +1,9 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class LoginResponse( + val token: String, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Member.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Member.kt new file mode 100644 index 00000000..dcf70873 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/Member.kt @@ -0,0 +1,38 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class Member( + val id: Int, + val firstName: String = "", + val lastName: String = "", + val clubId: Int? = null, + val active: Boolean = true, + val birthDate: String? = null, + val gender: String? = null, + val ttr: Int? = null, + val qttr: Int? = null, + val street: String? = null, + val city: String? = null, + val postalCode: String? = null, + val phone: String? = null, + val email: String? = null, + val testMembership: Boolean? = null, + val picsInInternetAllowed: Boolean? = null, + val memberFormHandedOver: Boolean? = null, + val adultReleaseApproved: Boolean? = null, + val adultReserveApproved: Boolean? = null, + val lastTraining: String? = null, + val trainingParticipations: Int? = null, + val notInTraining: Boolean? = null, + val missedTrainingWeeks: Int? = null, + val contacts: List = emptyList(), + val images: List = emptyList(), + val primaryImageId: Int? = null, + val primaryImageUrl: String? = null, + val imageUrl: String? = null, + val hasImage: Boolean? = null, + val myTischtennisPlayerId: String? = null, + val myTischtennisHistoryPlayerId: String? = null, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberActivityDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberActivityDtos.kt new file mode 100644 index 00000000..c7324ccb --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberActivityDtos.kt @@ -0,0 +1,20 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonArray + +@Serializable +data class MemberActivityStatDto( + val name: String? = null, + val code: String? = null, + val count: Int = 0, + val dates: JsonArray? = null, +) + +@Serializable +data class MemberLastParticipationDto( + val activityName: String? = null, + val activityFullName: String? = null, + val date: String? = null, + val diaryDateId: Int? = null, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberContactDto.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberContactDto.kt new file mode 100644 index 00000000..dbf47730 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberContactDto.kt @@ -0,0 +1,14 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class MemberContactDto( + val id: Int? = null, + val memberId: Int? = null, + val type: String, + val value: String = "", + val isParent: Boolean = false, + val parentName: String? = null, + val isPrimary: Boolean = false, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberGroupPhotoDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberGroupPhotoDtos.kt new file mode 100644 index 00000000..de3e531f --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberGroupPhotoDtos.kt @@ -0,0 +1,19 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class MemberGroupPhotoListResponse( + val success: Boolean = true, + val photos: List = emptyList(), +) + +@Serializable +data class MemberGroupPhotoDto( + val id: Int, + val clubId: Int? = null, + val title: String? = null, + val description: String? = null, + /** Relativer Pfad inkl. Query (Cache-Buster), z. B. `/api/member-group-photos/1/2/image?t=…` */ + val imageUrl: String? = null, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberImageDto.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberImageDto.kt new file mode 100644 index 00000000..150d0099 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberImageDto.kt @@ -0,0 +1,12 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class MemberImageDto( + val id: Int? = null, + val memberId: Int? = null, + val fileName: String? = null, + val sortOrder: Int? = null, + val url: String? = null, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetBody.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetBody.kt new file mode 100644 index 00000000..0ed94bd4 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetBody.kt @@ -0,0 +1,39 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class MemberContactSetBody( + val type: String, + val value: String, + val isParent: Boolean = false, + val parentName: String? = null, + val isPrimary: Boolean = false, +) + +/** + * Body für [POST /api/clubmembers/set/:clubId] – Feldnamen wie Web (`firstname`/`lastname`/`birthdate`). + */ +@Serializable +data class MemberSetBody( + val id: Int? = null, + @SerialName("firstname") val firstname: String, + @SerialName("lastname") val lastname: String, + val street: String? = "", + val city: String? = "", + val postalCode: String? = null, + @SerialName("birthdate") val birthdate: String? = null, + val phone: String? = "", + val email: String? = "", + val active: Boolean = true, + val testMembership: Boolean = false, + val picsInInternetAllowed: Boolean = false, + val gender: String? = "unknown", + val ttr: Int? = null, + val qttr: Int? = null, + val memberFormHandedOver: Boolean = false, + val adultReleaseApproved: Boolean = false, + val adultReserveApproved: Boolean = false, + val contacts: List = emptyList(), +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetMappers.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetMappers.kt new file mode 100644 index 00000000..04ded9c6 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/MemberSetMappers.kt @@ -0,0 +1,49 @@ +package de.tt_tagebuch.shared.api.models + +fun Member.toSetBody( + firstname: String, + lastname: String, + street: String?, + city: String?, + postalCode: String?, + birthdate: String?, + phone: String?, + email: String?, + active: Boolean, + testMembership: Boolean, + picsInInternetAllowed: Boolean, + gender: String?, + memberFormHandedOver: Boolean, + adultReleaseApproved: Boolean, + adultReserveApproved: Boolean, + contacts: List, +): MemberSetBody = MemberSetBody( + id = if (id <= 0) null else id, + firstname = firstname, + lastname = lastname, + street = street.orEmpty(), + city = city.orEmpty(), + postalCode = postalCode?.ifBlank { null }, + birthdate = birthdate?.ifBlank { null }, + phone = phone.orEmpty(), + email = email.orEmpty(), + active = active, + testMembership = testMembership, + picsInInternetAllowed = picsInInternetAllowed, + gender = gender?.ifBlank { null } ?: "unknown", + ttr = ttr, + qttr = qttr, + memberFormHandedOver = memberFormHandedOver, + adultReleaseApproved = adultReleaseApproved, + adultReserveApproved = adultReserveApproved, + contacts = contacts, +) + +fun MemberContactDto.toSetBody(): MemberContactSetBody = + MemberContactSetBody( + type = type, + value = value, + isParent = isParent, + parentName = parentName, + isPrimary = isPrimary, + ) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ParticipantStatusRequest.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ParticipantStatusRequest.kt new file mode 100644 index 00000000..6c1cd74f --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/ParticipantStatusRequest.kt @@ -0,0 +1,8 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class ParticipantStatusRequest( + val attendanceStatus: String, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PredefinedActivityDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PredefinedActivityDtos.kt new file mode 100644 index 00000000..5fd039d3 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PredefinedActivityDtos.kt @@ -0,0 +1,22 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class PredefinedActivityDto( + val id: Int, + val name: String? = null, + val code: String? = null, + val description: String? = null, + val duration: Int? = null, + val durationText: String? = null, + val excludeFromStats: Boolean? = null, +) + +fun PredefinedActivityDto.displayLabel(): String { + val n = name?.trim().orEmpty() + if (n.isNotEmpty()) return n + val c = code?.trim().orEmpty() + if (c.isNotEmpty()) return c + return "Übung $id" +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PublicAuthDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PublicAuthDtos.kt new file mode 100644 index 00000000..03b3dee4 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/PublicAuthDtos.kt @@ -0,0 +1,25 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class RegisterRequest( + val email: String, + val password: String, +) + +@Serializable +data class ForgotPasswordRequest( + val email: String, +) + +@Serializable +data class MessageResponse( + val message: String? = null, +) + +@Serializable +data class ResetPasswordRequest( + val token: String, + val password: String, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/SessionStatusResponse.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/SessionStatusResponse.kt new file mode 100644 index 00000000..f07120b2 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/SessionStatusResponse.kt @@ -0,0 +1,10 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class SessionStatusResponse( + val valid: Boolean, + val message: String? = null, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingGroupDtos.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingGroupDtos.kt new file mode 100644 index 00000000..586f2db1 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingGroupDtos.kt @@ -0,0 +1,24 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class TrainingTimeDto( + val id: Int = 0, + val trainingGroupId: Int? = null, + val weekday: Int = 0, + val startTime: String = "", + val endTime: String = "", + val sortOrder: Int = 0, +) + +@Serializable +data class TrainingGroupDto( + val id: Int = 0, + val clubId: Int? = null, + val name: String = "", + val sortOrder: Int = 0, + val isPreset: Boolean = false, + val presetType: String? = null, + val trainingTimes: List = emptyList(), +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingStats.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingStats.kt new file mode 100644 index 00000000..4094b542 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/TrainingStats.kt @@ -0,0 +1,101 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable + +@Serializable +data class TrainingStats( + val members: List = emptyList(), + val trainingsCount12Months: Int = 0, + val trainingsCount3Months: Int = 0, + val trainingDays: List = emptyList(), + val overview: TrainingStatsOverview = TrainingStatsOverview(), + val weekdayStats: List = emptyList(), + val monthlyTrend: List = emptyList(), + val memberDistribution: TrainingStatsMemberDistribution = TrainingStatsMemberDistribution(), +) + +@Serializable +data class TrainingStatsOverview( + val activeMembersCount: Int = 0, + val totalParticipants12Months: Int = 0, + val averageParticipants12Months: Double = 0.0, + val attendanceRate12Months: Double = 0.0, + val inactiveMembersCount: Int = 0, + val bestTrainingDay: TrainingStatsDay? = null, +) + +@Serializable +data class TrainingStatsMemberDistribution( + val highlyActive: Int = 0, + val regular: Int = 0, + val occasional: Int = 0, + val inactive: Int = 0, +) + +@Serializable +data class TrainingStatsWeekdayBucket( + val weekday: String = "", + val weekdayIndex: Int = 0, + val trainingCount: Int = 0, + val participantCount: Int = 0, + val averageParticipants: Double = 0.0, +) + +@Serializable +data class TrainingStatsMonthlyTrend( + val key: String = "", + val label: String = "", + val trainingCount: Int = 0, + val participantCount: Int = 0, + val averageParticipants: Double = 0.0, +) + +@Serializable +data class TrainingStatsTrainingGroup( + val id: Int = 0, + val name: String = "", +) + +@Serializable +data class TrainingStatsTrainingDetail( + val id: Int = 0, + val date: String? = null, + val activityName: String? = null, + val startTime: String? = null, + val endTime: String? = null, +) + +@Serializable +data class TrainingStatsMember( + val id: Int, + val firstName: String = "", + val lastName: String = "", + val birthDate: String? = null, + val ttr: Int? = null, + val qttr: Int? = null, + val participation12Months: Int = 0, + val participation3Months: Int = 0, + val participationTotal: Int = 0, + val participationRate12Months: Double = 0.0, + val lastTraining: String? = null, + val lastTrainingTs: Long = 0, + val missedTrainingWeeks: Int = 0, + val notInTraining: Boolean = false, + val trainingGroups: List = emptyList(), + val trainingDetails: List = emptyList(), +) + +@Serializable +data class TrainingStatsDay( + val id: Int, + val date: String, + val participantCount: Int = 0, + val participants: List = emptyList(), +) + +@Serializable +data class TrainingStatsParticipant( + val id: Int, + val firstName: String = "", + val lastName: String = "", +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/UserClubPermissions.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/UserClubPermissions.kt new file mode 100644 index 00000000..903b258e --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/api/models/UserClubPermissions.kt @@ -0,0 +1,12 @@ +package de.tt_tagebuch.shared.api.models + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonObject + +@Serializable +data class UserClubPermissions( + val role: String, + val isOwner: Boolean, + val permissions: JsonObject = JsonObject(emptyMap()), +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/MobileStrings.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/MobileStrings.kt new file mode 100644 index 00000000..2afa8c08 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/MobileStrings.kt @@ -0,0 +1,35912 @@ +package de.tt_tagebuch.shared.i18n + +data class SupportedLanguage(val code: String, val label: String) + +object MobileStrings { + const val DEFAULT_LANGUAGE = "de" + + val supportedLanguages: List = listOf( + SupportedLanguage("de-CH", "Deutsch (Schweiz)"), + SupportedLanguage("de-extended", "Deutsch (erweitert)"), + SupportedLanguage("de", "Deutsch"), + SupportedLanguage("en-AU", "English (AU)"), + SupportedLanguage("en-GB", "English (UK)"), + SupportedLanguage("en-US", "English (US)"), + SupportedLanguage("es", "Español"), + SupportedLanguage("fil", "Filipino"), + SupportedLanguage("fr", "Français"), + SupportedLanguage("it", "Italiano"), + SupportedLanguage("ja", "日本語"), + SupportedLanguage("pl", "Polski"), + SupportedLanguage("th", "ไทย"), + SupportedLanguage("tl", "Tagalog"), + SupportedLanguage("zh", "中文"), + ) + + private val de_CH: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Trainingstagebuch", + "app.title" to "Trainingstagebuch", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Aktivierig fehlgschlage. Bitte de Link überprüefe.", + "auth.confirmPassword" to "Passwort bestätige", + "auth.email" to "E-Mail", + "auth.forgotPassword" to "Passwort vergesse?", + "auth.forgotPasswordDescription" to "Gib dini E-Mail-Adrässe ii. Du bechunsch en Link, zum dis Passwort zruggsetze.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Aamelde", + "auth.loginFailed" to "Login fehlgschlage. Bitte Zuegangsdaten prüefe.", + "auth.loginSuccess" to "Erfolgriich aagmeldet", + "auth.logout" to "Abmelde", + "auth.logoutSuccess" to "Erfolgriich abgmeldet", + "auth.newPassword" to "Nöis Passwort", + "auth.noAccount" to "No kei Konto?", + "auth.password" to "Passwort", + "auth.passwordResetSuccess" to "Dis Passwort isch erfolgriich gänderet worde. Du chasch dich jetzt aaloge.", + "auth.passwordsDoNotMatch" to "D'Passwörter stimed nöd überein.", + "auth.passwordTooShort" to "S'Passwort muess mindestens 6 Zeiche lang sii.", + "auth.register" to "Registriere", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Aagmeldet bliibe", + "auth.resetEmailSent" to "Falls es Konto mit dere E-Mail git, isch en Link zum Zruggsetze gschickt worde. Bitte prüef dis Postfach (au de Spam-Ordner).", + "auth.resetFailed" to "Passwort het nöd chönne gänderet werde. De Link isch villicht abglaufe.", + "auth.resetPassword" to "Nöis Passwort vergee", + "auth.resetRequestFailed" to "Aafrag fehlgschlage. Bitte probiers nomal.", + "auth.saveNewPassword" to "Passwort speichere", + "auth.saving" to "Wird gspeicheret...", + "auth.sending" to "Wird gschickt...", + "auth.sendResetLink" to "Link schicke", + "auth.sessionExpired" to "Dini Sitzig isch abglaufe. Du wirsch abgmeldet.", + "auth.toLogin" to "Zum Login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Zugriff uf de Verein verweigeret.", + "club.accessRequested" to "Zugriff isch aagfragt worde.", + "club.accessRequestFailed" to "Zugriffsafrag het nöd chönne gstellt werde.", + "club.accessRequestPending" to "Der Zugriff auf diesen Verein ist beantragt. Bitte haben Sie etwas Geduld.", + "club.create" to "Verein erstelle", + "club.createTitle" to "Verein aalege", + "club.diary" to "Trainingstagebuch", + "club.errorLoadingRequests" to "Fehler bim Lade vo de offene Afroge", + "club.load" to "Lade", + "club.members" to "Mitglieder", + "club.mobileSelectHint" to "Bitte wähl zerscht en Verein us, zum d App uf em Smartphone z nutze.", + "club.name" to "Vereinsname", + "club.new" to "Neue Verein", + "club.noAccess" to "Für diesen Verein isch no kei Zugriff freigschaltet.", + "club.openAccessRequests" to "Offeni Zugriffsafrage", + "club.openRequests" to "Offeni Zugriffsafrage", + "club.requestAccess" to "Zugriff beantrage", + "club.select" to "Verein uswähle", + "club.selectPlaceholder" to "Verein wähle...", + "club.title" to "Verein", + "club.trainingDiary" to "Trainingstagebuch", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Aktione", + "common.active" to "Aktiv", + "common.add" to "Hinzufüge", + "common.all" to "Alle", + "common.apply" to "Aawände", + "common.back" to "Zrugg", + "common.cancel" to "Abbreche", + "common.choose" to "Wähle", + "common.clear" to "Lösche", + "common.close" to "Schliessen", + "common.confirm" to "Bestätige", + "common.create" to "Erstelle", + "common.date" to "Datum", + "common.days" to "Täg", + "common.delete" to "Lösche", + "common.description" to "Beschrieb", + "common.details" to "Details", + "common.disabled" to "Deaktiviert", + "common.download" to "Herunterlade", + "common.edit" to "Bearbeite", + "common.enabled" to "Aktiviert", + "common.filter" to "Filter", + "common.hours" to "Stunde", + "common.in" to "in", + "common.inactive" to "Inaktiv", + "common.loading" to "Lade...", + "common.min" to "Min", + "common.minutes" to "Minute", + "common.months" to "Monat", + "common.move" to "Verschiebe", + "common.name" to "Name", + "common.new" to "Neu", + "common.next" to "Wiiter", + "common.no" to "Nei", + "common.ok" to "OK", + "common.optional" to "Optional", + "common.period" to "Zeitraum", + "common.previous" to "Zrugg", + "common.refresh" to "Neu lade", + "common.remove" to "Entferne", + "common.required" to "Erforderlich", + "common.reset" to "Zruggsetze", + "common.save" to "Speichere", + "common.saved" to "Gespeichert", + "common.saving" to "Speichere...", + "common.search" to "Suche", + "common.select" to "Uswähle", + "common.status" to "Status", + "common.submit" to "Abschicke", + "common.time" to "Zyt", + "common.today" to "Hüt", + "common.type" to "Typ", + "common.update" to "Aktualisiere", + "common.view" to "Aazeige", + "common.weeks" to "Wuche", + "common.years" to "Jahr", + "common.yes" to "Ja", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Schlag hinzufügen", + "courtDrawingTool.backhand" to "Rückhand", + "courtDrawingTool.codeLabel" to "Kürzel", + "courtDrawingTool.completeFirstStroke" to "1. Schlag vervollständigen", + "courtDrawingTool.completeFirstStrokeHint" to "Wähle Startposition, Schlagseite, Rotation und Ziel. Danach erscheint die grafische Darstellung.", + "courtDrawingTool.configureExercise" to "Übung konfigurieren", + "courtDrawingTool.counterSpin" to "Gegenläufer", + "courtDrawingTool.forehand" to "Vorhand", + "courtDrawingTool.noAdditionalStrokes" to "Noch keine Folgeschläge angelegt.", + "courtDrawingTool.preview" to "Vorschau", + "courtDrawingTool.previewHint" to "Die Grafik erscheint, sobald der erste Schlag vollständig gesetzt ist.", + "courtDrawingTool.service" to "Aufschlag:", + "courtDrawingTool.serviceTitle" to "Aufschlag", + "courtDrawingTool.sidespin" to "Seitschnitt", + "courtDrawingTool.sideUnderspin" to "Seitunterschnitt", + "courtDrawingTool.spin" to "Schnitt:", + "courtDrawingTool.startLeft" to "links", + "courtDrawingTool.startMiddle" to "mitte", + "courtDrawingTool.startRight" to "rechts", + "courtDrawingTool.stepAdditionalStrokes" to "4. Folgeschläge", + "courtDrawingTool.stepAdditionalStrokesHint" to "Optional weitere Bälle als Liste aufbauen.", + "courtDrawingTool.stepFirstStroke" to "2. 1. Schlag", + "courtDrawingTool.stepFirstStrokeHint" to "Schlagseite und Rotation für den ersten Ball festlegen.", + "courtDrawingTool.stepStartPosition" to "1. Startposition", + "courtDrawingTool.stepStartPositionHint" to "Aus welcher Aufschlagposition startet die Übung?", + "courtDrawingTool.stepTarget" to "3. Ziel", + "courtDrawingTool.stepTargetHint" to "Zielzone für den ersten Schlag auswählen.", + "courtDrawingTool.strokeSide" to "Seite", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Schnittabwehr", + "courtDrawingTool.strokeTypeCounter" to "Konter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Schlagart", + "courtDrawingTool.strokeTypeLobDefense" to "Ballonabwehr", + "courtDrawingTool.strokeTypePush" to "Schupf", + "courtDrawingTool.strokeTypeSmash" to "Schuss", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Rückhand halblang", + "courtDrawingTool.targetBackhandLong" to "Rückhand lang", + "courtDrawingTool.targetBackhandShort" to "Rückhand kurz", + "courtDrawingTool.targetForehandHalfLong" to "Vorhand halblang", + "courtDrawingTool.targetForehandLong" to "Vorhand lang", + "courtDrawingTool.targetForehandShort" to "Vorhand kurz", + "courtDrawingTool.targetMiddleHalfLong" to "Mitte halblang", + "courtDrawingTool.targetMiddleLong" to "Mitte lang", + "courtDrawingTool.targetMiddleShort" to "Mitte kurz", + "courtDrawingTool.targetPosition" to "Zielposition:", + "courtDrawingTool.targetPositionLabel" to "Zielposition", + "courtDrawingTool.title" to "Tischtennis-Übungszeichnung", + "courtDrawingTool.titleLabel" to "Titel", + "courtDrawingTool.topspin" to "Überschnitt", + "courtDrawingTool.toTarget" to "nach", + "courtDrawingTool.underspin" to "Unterschnitt", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Aktiver Trainingstag", + "diary.activities" to "Aktivitäten", + "diary.activity" to "Aktivität", + "diary.activityDrawing" to "Aktivitätszeichnung", + "diary.activityImage" to "Aktivitätsbild", + "diary.activityNotFound" to "Aktivität nicht gefunden. Bitte wählen Sie eine aus der Liste aus.", + "diary.activityOrTimeblock" to "Aktivität / Zeitblock", + "diary.activityPlaceholder" to "Aktivität", + "diary.activityRequired" to "Bitte geben Sie eine Aktivität ein.", + "diary.addActivity" to "Aktivität hinzufügen", + "diary.addGroup" to "Gruppe hinzufügen", + "diary.addGroupActivity" to "Gruppen-Aktivität hinzufügen", + "diary.addGroupButton" to "+ Gruppe", + "diary.addTimeblock" to "Zeitblock", + "diary.all" to "Alle", + "diary.applySuggestion" to "Vorschlag übernehmen", + "diary.assignParticipants" to "Teilnehmer zuordnen", + "diary.assignParticipantsForGroupActivity" to "Teilnehmer für Gruppen-Aktivität zuordnen", + "diary.assignShort" to "Zuordnen", + "diary.bookAccident" to "Unfall buchen", + "diary.confirmDelete" to "Löschen bestätigen", + "diary.confirmDeleteDate" to "Möchten Sie dieses Datum wirklich löschen?", + "diary.confirmDeleteDateDetails" to "Alle zugehörigen Daten werden ebenfalls gelöscht.", + "diary.confirmDeleteGroup" to "Möchten Sie die Gruppe \"{name}\" wirklich löschen?", + "diary.createDate" to "Datum anlegen", + "diary.createDrawing" to "Übungszeichnung erstellen", + "diary.createGroups" to "Gruppen erstellen", + "diary.createNew" to "Neu anlegen", + "diary.createNewDate" to "Neues Datum anlegen", + "diary.date" to "Datum", + "diary.dateCannotBeDeleted" to "Datum kann nicht gelöscht werden", + "diary.dateCannotBeDeletedDetails" to "Es sind noch Inhalte vorhanden (Trainingplan, Teilnehmer, Aktivitäten, Unfälle oder Notizen).", + "diary.dateNoLongerCurrent" to "Ausgewähltes Datum war nicht mehr aktuell. Bitte erneut versuchen.", + "diary.delete" to "Löschen", + "diary.deleteDate" to "Datum löschen", + "diary.deleteGroup" to "Gruppe löschen", + "diary.duration" to "Dauer", + "diary.durationExampleLong" to "z.B. 2x7 oder 3*5", + "diary.durationExampleShort" to "z.B. 2x7", + "diary.durationMinutes" to "Dauer (Min)", + "diary.editActivity" to "Aktivität bearbeiten", + "diary.editGroupActivity" to "Gruppen-Aktivität bearbeiten", + "diary.editTrainingTimes" to "Trainingszeiten bearbeiten", + "diary.errorCreatingActivity" to "Fehler beim Erstellen der Aktivität", + "diary.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen", + "diary.errorDeletingGroup" to "Fehler beim Löschen der Gruppe", + "diary.errorLoadingPredefinedActivities" to "Fehler beim Laden der vordefinierten Aktivitäten", + "diary.errorMarkingForm" to "Fehler beim Markieren des Mitgliedsformulars", + "diary.errorOccurred" to "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "diary.existingGroups" to "Vorhandene Gruppen", + "diary.filterAbsent" to "Abwesend", + "diary.filterAll" to "Alle", + "diary.filterExcused" to "Entschuldigt", + "diary.filterPresent" to "Anwesend", + "diary.filterTest" to "Probe", + "diary.formHandedOver" to "Mitgliedsformular ausgehändigt", + "diary.formMarkedAsHandedOver" to "Mitgliedsformular als ausgehändigt markiert", + "diary.freeActivities" to "Freie Aktivitäten", + "diary.gallery" to "Mitglieder-Galerie", + "diary.galleryCreating" to "Galerie wird erstellt…", + "diary.group" to "Gruppe...", + "diary.groupDeletedSuccessfully" to "Gruppe wurde erfolgreich gelöscht!", + "diary.groupManagement" to "Gruppenverwaltung", + "diary.groupsCreated" to "{count} Gruppen wurden erfolgreich erstellt!", + "diary.groupsLabel" to "Gruppen", + "diary.groupsSection" to "Gruppen", + "diary.leader" to "Leiter", + "diary.min" to "Min", + "diary.minutes" to "Minuten", + "diary.mustCreateAtLeastTwoGroups" to "Beim ersten Erstellen müssen mindestens 2 Gruppen erstellt werden!", + "diary.nextAppointment" to "Nächster Termin", + "diary.noActiveTrainingDay" to "Kein Trainingstag ausgewählt.", + "diary.noEntries" to "Keine Einträge", + "diary.noFreeActivitiesYet" to "Noch keine freien Aktivitäten erfasst.", + "diary.noParticipants" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "diary.numberOfGroups" to "Anzahl Gruppen", + "diary.oneGroupAdded" to "1 Gruppe wurde erfolgreich hinzugefügt!", + "diary.openPlanItems" to "{count} offen", + "diary.openPlanItemsLabel" to "Plan-Status", + "diary.overallActivity" to "Gesamt-Aktivität", + "diary.participants" to "Teilnehmer", + "diary.participantStatusCancelled" to "Abgesagt", + "diary.participantStatusExcused" to "Entschuldigt", + "diary.participantStatusNone" to "Kein Status", + "diary.planActivitiesCount" to "Plan-Aktivitäten", + "diary.planAddHint" to "Neue Plan-Elemente fügst du über die Aktionen oben hinzu.", + "diary.planEmptyState" to "Im Trainingsplan ist noch nichts eingetragen.", + "diary.quickAdd" to "+ Schnell hinzufügen", + "diary.searchParticipants" to "Teilnehmer suchen", + "diary.selectGroup" to "Gruppe auswählen...", + "diary.selectGroupAndActivity" to "Bitte wählen Sie eine Gruppe und geben Sie eine Aktivität ein.", + "diary.selectParticipantAndNote" to "Bitte wählen Sie einen Teilnehmer aus und geben Sie einen Notiztext ein.", + "diary.selectTags" to "Tags auswählen", + "diary.selectTrainingGroup" to "Trainingsgruppe auswählen", + "diary.selectTrainingGroupPlaceholder" to "Bitte wählen...", + "diary.showImage" to "Bild/Zeichnung anzeigen", + "diary.skipSuggestion" to "Ohne Vorschlag fortfahren", + "diary.standardActivities" to "Standard-Aktivitäten", + "diary.standardActivityAddError" to "Standard-Aktivität konnte nicht hinzugefügt werden.", + "diary.standardDurationShort" to "Min", + "diary.startTime" to "Startzeit", + "diary.statusEmpty" to "Dieser Trainingstag ist noch leer.", + "diary.statusInProgress" to "Dieser Trainingstag ist teilweise vorbereitet.", + "diary.statusOpenShort" to "Offen", + "diary.statusReady" to "Zeiten und Trainingsplan sind gepflegt.", + "diary.statusReadyShort" to "Bereit", + "diary.suggestion" to "Vorschlag", + "diary.timeblock" to "Zeitblock", + "diary.timeblocksCount" to "Zeitblöcke", + "diary.title" to "Trainingstagebuch", + "diary.today" to "Heute", + "diary.trainingDayAsPDF" to "Trainingstag als PDF herunterladen", + "diary.trainingDayAsPDFShort" to "Trainingstag als PDF", + "diary.trainingDayChecklist" to "Abschlusscheck", + "diary.trainingDaySection" to "Trainingstag", + "diary.trainingDaySummaryPdfShort" to "Teilnehmerübersicht als PDF", + "diary.trainingEnd" to "Trainingsende", + "diary.trainingPlan" to "Trainingsplan", + "diary.trainingPlanAsPDF" to "Trainingsplan als PDF", + "diary.trainingPlanPdfShort" to "Ablaufplan als PDF", + "diary.trainingStart" to "Trainingsbeginn", + "diary.trainingTimesUpdated" to "Trainingszeiten erfolgreich aktualisiert.", + "diary.trainingWindow" to "Trainingsfenster", + "diary.trainingWindowUnset" to "Noch nicht gesetzt", + "diary.unassignedPlanItems" to "{count} offen", + "diary.updateTimes" to "Zeiten aktualisieren", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Dütsch)", + "languages.de-CH" to "Schwiizerdütsch", + "languages.en-AU" to "English (Englisch (AU))", + "languages.en-GB" to "English (Englisch (GB))", + "languages.en-US" to "English (Englisch (US))", + "languages.es" to "Español (Spanisch)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (Französisch)", + "languages.it" to "Italiano (Italienisch)", + "languages.ja" to "日本語 (Japanisch)", + "languages.pl" to "Polski (Polnisch)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Chinesisch)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Aktionen", + "members.active" to "Aktiv", + "members.activeMembers" to "Aktive Mitglieder", + "members.addEmail" to "E-Mail-Adresse hinzufügen", + "members.addGroup" to "Gruppe hinzufügen...", + "members.addPhone" to "Telefonnummer hinzufügen", + "members.address" to "Adresse", + "members.adultReleaseApproved" to "Freigabe Erwachsene", + "members.adultReserveApproved" to "Ersatz bei Erwachsenen", + "members.adults" to "Erwachsene (18+)", + "members.age" to "Alter", + "members.ageFromPlaceholder" to "von", + "members.ageGroup" to "Altersklasse", + "members.ageRange" to "Alter von - bis", + "members.ageToPlaceholder" to "bis", + "members.batchFormsMarkedEmpty" to "In der aktuellen Auswahl gibt es keine ungeprüften Formulare.", + "members.batchFormsMarkedSuccess" to "{count} Formular(e) als geprüft markiert.", + "members.batchMarkedRegularEmpty" to "In der aktuellen Auswahl gibt es keine Probe-Mitglieder.", + "members.batchMarkedRegularSuccess" to "{count} Probe-Mitglied(er) als regulär markiert.", + "members.batchPartialFailure" to "{success} erfolgreich, {failed} fehlgeschlagen.", + "members.birthdate" to "Geburtsdatum", + "members.bulkActions" to "Sammelaktionen", + "members.camera" to "Kamera", + "members.change" to "Ändern", + "members.city" to "Ort", + "members.clearFields" to "Felder leeren", + "members.clearFilters" to "Filter zurücksetzen", + "members.clickTtRequestAction" to "Spielberechtigung in click-TT beantragen", + "members.clickTtRequestConfirm" to "Soll für {name} der automatisierte Click-TT-Antrag gestartet werden?", + "members.clickTtRequestError" to "Der Click-TT-Antrag konnte nicht eingereicht werden.", + "members.clickTtRequestFailedLog" to "Click-TT-Antrag fehlgeschlagen", + "members.clickTtRequestHint" to "Der Antrag wird im Backend automatisiert durch den Click-TT-Workflow geführt.", + "members.clickTtRequestPending" to "Click-TT-Antrag läuft", + "members.clickTtRequestPendingShort" to "Läuft ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Bitte loggen Sie sich noch auf click-tt ein und senden Sie den Antrag ab.", + "members.clickTtRequestTitle" to "Click-TT-Antrag starten", + "members.clickTtSubmitted" to "Click-TT gestellt", + "members.closeEditor" to "Editor schließen", + "members.composeEmail" to "E-Mail vorbereiten", + "members.contact" to "Kontakt", + "members.copyContactSummary" to "Kontaktübersicht kopieren", + "members.copyContactSummarySuccess" to "Kontaktübersicht in die Zwischenablage kopiert.", + "members.copyEmails" to "E-Mails kopieren", + "members.copyEmailsEmpty" to "In der aktuellen Auswahl gibt es keine E-Mail-Adressen.", + "members.copyEmailsSuccess" to "E-Mail-Liste in die Zwischenablage kopiert.", + "members.copyPhones" to "Telefone kopieren", + "members.copyPhonesEmpty" to "In der aktuellen Auswahl gibt es keine Telefonnummern.", + "members.copyPhonesSuccess" to "Telefonliste in die Zwischenablage kopiert.", + "members.create" to "Anlegen", + "members.createNewMember" to "Neues Mitglied anlegen", + "members.dataIssueAddress" to "Adresse fehlt", + "members.dataIssueBirthdate" to "Geburtsdatum fehlt", + "members.dataIssueCity" to "Ort fehlt", + "members.dataIssueEmail" to "E-Mail fehlt", + "members.dataIssueGender" to "Geschlecht ungeklärt", + "members.dataIssuePhone" to "Telefon fehlt", + "members.dataIssuePostalCode" to "PLZ fehlt", + "members.dataIssueStreet" to "Strasse fehlt", + "members.dataIssueTrainingGroup" to "Trainingsgruppe fehlt", + "members.dataQuality" to "Datenqualität", + "members.dataQualityComplete" to "Daten vollständig", + "members.deactivateMember" to "Mitglied deaktivieren", + "members.deactivateMemberConfirm" to "Möchten Sie \"{name}\" wirklich deaktivieren?", + "members.deactivateMemberTitle" to "Mitglied deaktivieren", + "members.deleteImageConfirm" to "Möchten Sie dieses Bild wirklich löschen?", + "members.deleteImageTitle" to "Bild löschen", + "members.editHint" to "Ein Klick auf eine Zeile öffnet den Editor.", + "members.editMember" to "Mitglied bearbeiten", + "members.editorAssignTrainingGroupHint" to "Bitte mindestens eine Trainingsgruppe zuordnen.", + "members.editorCreateHint" to "Neues Mitglied anlegen und Kontaktdaten direkt erfassen.", + "members.editorEditHint" to "Daten von {name} bearbeiten.", + "members.editorRecommendedEntry" to "Empfohlener Eintrag", + "members.emailAddress" to "E-Mail-Adresse", + "members.emailAddressShort" to "Email-Adresse", + "members.emails" to "E-Mail-Adressen", + "members.errorAddingToGroup" to "Fehler beim Hinzufügen zur Gruppe", + "members.errorDeactivatingMember" to "Fehler beim Deaktivieren des Mitglieds", + "members.errorDeletingImage" to "Bild konnte nicht gelöscht werden", + "members.errorLoadingImage" to "Fehler beim Laden des Bildes", + "members.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "members.errorLoadingTrainingParticipations" to "Fehler beim Laden der Trainingsteilnahmen", + "members.errorMarkingForm" to "Fehler beim Markieren des Formulars.", + "members.errorRemovingFromGroup" to "Fehler beim Entfernen aus der Gruppe", + "members.errorRemovingTestMembership" to "Fehler beim Entfernen der Testmitgliedschaft.", + "members.errorRotatingImage" to "Fehler beim Drehen des Bildes", + "members.errorSavingMember" to "Fehler beim Speichern des Mitglieds", + "members.errorSettingPrimaryImage" to "Hauptbild konnte nicht gesetzt werden", + "members.errorTransfer" to "Fehler bei der Übertragung", + "members.errorUpdatingRatings" to "Fehler beim Aktualisieren der TTR/QTTR-Werte", + "members.errorUploadingImage" to "Bild konnte nicht hochgeladen werden", + "members.exercises" to "Übungen", + "members.exportCsv" to "CSV exportieren", + "members.exportCsvFile" to "mitglieder-auswahl.csv", + "members.exportEmails" to "E-Mail", + "members.exportMembersSelected" to "Mitglieder aktuell ausgewählt", + "members.exportPhones" to "Telefon", + "members.exportPreview" to "Exportvorschau", + "members.exportPreviewEmpty" to "Keine Mitglieder in der aktuellen Auswahl", + "members.exportPreviewNames" to "Vorschau", + "members.exportReachableByEmail" to "mit E-Mail-Adresse", + "members.exportReachableByPhone" to "mit Telefonnummer", + "members.firstName" to "Vorname", + "members.formHandedOver" to "Mitgliedsformular ausgehändigt", + "members.formMarkedAsHandedOver" to "Mitgliedsformular als ausgehändigt markiert.", + "members.gender" to "Geschlecht", + "members.genderDiverse" to "Divers", + "members.genderFemale" to "Weiblich", + "members.genderMale" to "Männlich", + "members.genderUnknown" to "Unbekannt", + "members.generatePhoneList" to "Telefonliste generieren", + "members.groupPhotoCrop" to "Gruppenfoto verarbeiten", + "members.groupPhotoCropSaved" to "Mitgliedsfoto aus Gruppenfoto gespeichert.", + "members.image" to "Bild", + "members.imageDeleted" to "Bild wurde gelöscht.", + "members.imageInternet" to "Bild (Inet?)", + "members.imagePreview" to "Vorschau des Mitgliedsbildes", + "members.imageUpdated" to "Bild wurde aktualisiert", + "members.inactive" to "inaktiv", + "members.inactiveMembers" to "Inaktive Mitglieder", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17 (17 und jünger)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Nachname", + "members.lastTrainingFilter" to "Letztes Training", + "members.lastTrainingFilterHasDate" to "Mit erfasstem Datum", + "members.lastTrainingFilterHint" to "AK-Spalte: laufende und nächste Saison. Weitere Details (letztes Training, Teilnahmen) auch beim Überfahren der Zeile.", + "members.lastTrainingFilterNoDate" to "Ohne letztes Training", + "members.lastTrainingFilterNotInTraining" to "„Nicht mehr im Training“", + "members.loadingMembers" to "Lade Mitglieder...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Formular geprüft", + "members.markFormsForSelection" to "Formulare für Auswahl prüfen", + "members.markRegular" to "Als regulär markieren", + "members.markRegularForSelection" to "Auswahl als regulär markieren", + "members.memberDeactivated" to "Mitglied deaktiviert", + "members.memberDetails" to "Mitgliedsdetails", + "members.memberFormHandedOver" to "Mitgliedsformular ausgehändigt", + "members.memberImage" to "Mitgliedsbild", + "members.memberImages" to "Mitgliedsbilder", + "members.memberInfo" to "Mitglieder-Info", + "members.memberScope" to "Mitgliedsbereich", + "members.missingTtrHistoryId" to "Keine myTischtennis-ID hinterlegt", + "members.name" to "Name, Vorname", + "members.newMember" to "Neues Mitglied", + "members.noAddressShort" to "Keine Adresse", + "members.noEmailShort" to "Keine E-Mail", + "members.noGroupsAssigned" to "Keine Gruppen zugeordnet", + "members.noGroupsAvailable" to "Keine Gruppen verfügbar", + "members.noOpenTasks" to "Keine offenen Aufgaben", + "members.noPhoneShort" to "Kein Telefon", + "members.notes" to "Notizen", + "members.noTestMembership" to "Keine Testmitgliedschaft mehr", + "members.notInTrainingTooltip" to "Seit {weeks} Trainingswochen ohne Teilnahme", + "members.onlyActiveMembers" to "Es werden nur aktive Mitglieder ausgegeben", + "members.openTasks" to "Offene Aufgaben", + "members.parent" to "Elternteil", + "members.parentFallback" to "Elternteil", + "members.parentName" to "Name (z.B. Mutter, Vater)", + "members.phoneList" to "Telefonliste.pdf", + "members.phoneListForSelection" to "Telefonliste für Auswahl", + "members.phoneListSelectionFile" to "Telefonliste-Auswahl.pdf", + "members.phoneNumber" to "Telefonnummer", + "members.phoneNumberShort" to "Telefon-Nr.", + "members.phones" to "Telefonnummern", + "members.picsInInternetAllowed" to "Pics in Internet erlaubt", + "members.postalCode" to "PLZ", + "members.previewLastTraining" to "Letztes Training", + "members.previewNoLastTraining" to "Noch keine Teilnahme", + "members.primary" to "Primär", + "members.primaryImageUpdated" to "Hauptbild wurde aktualisiert.", + "members.remove" to "Entfernen", + "members.resultsVisible" to "Mitglieder sichtbar", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Aktiv", + "members.scopeActiveDataIncomplete" to "Aktiv + Daten unvollständig", + "members.scopeActiveTest" to "Aktiv + Probe", + "members.scopeAll" to "Alle", + "members.scopeDataIncomplete" to "Daten unvollständig", + "members.scopeInactive" to "Inaktiv", + "members.scopeNeedsForm" to "Formular ungeprüft", + "members.scopeNotTraining" to "Nicht mehr im Training", + "members.scopeTest" to "Probe", + "members.search" to "Suche", + "members.searchAndFilter" to "Suche und Filter", + "members.searchPlaceholder" to "Name, Ort, Telefon oder E-Mail suchen", + "members.selectFile" to "Datei auswählen", + "members.showInactiveMembers" to "Inaktive Mitglieder anzeigen", + "members.showTrainingParticipationsColumn" to "Spalte „Trainingsteilnahmen“ anzeigen", + "members.showTtrHistory" to "TTR-Historie anzeigen", + "members.sixOrMoreParticipations" to "6 oder mehr Trainingsteilnahmen", + "members.sortAge" to "Alter", + "members.sortBirthday" to "Geburtstag", + "members.sortBy" to "Sortieren nach", + "members.sortFirstName" to "Vorname", + "members.sortLastName" to "Nachname", + "members.sortLastTraining" to "Letztes Training", + "members.sortOpenTasks" to "Offene Aufgaben", + "members.sortQttr" to "QTTR-Wert", + "members.status" to "Status", + "members.street" to "Straße", + "members.subtitle" to "Mitglieder suchen, filtern und direkt bearbeiten.", + "members.taskActionMarkRegular" to "Regulär setzen", + "members.taskActionRequest" to "Anfragen", + "members.taskActionReview" to "Öffnen", + "members.taskActionVerify" to "Prüfen", + "members.taskAssignTrainingGroup" to "Trainingsgruppe zuordnen", + "members.taskCheckClickTt" to "Click-TT-Spielberechtigung prüfen", + "members.taskCheckDataQuality" to "Datenqualität prüfen", + "members.taskCheckTrainingStatus" to "Trainingsstatus prüfen", + "members.taskReviewTrialStatus" to "Probe-Status prüfen", + "members.taskVerifyForm" to "Formular prüfen", + "members.testMember" to "Testm.", + "members.testMembers" to "Testmitglieder", + "members.testMembership" to "Testmitgliedschaft", + "members.testMembershipRemoved" to "Testmitgliedschaft entfernt.", + "members.threeOrMoreParticipations" to "3 oder mehr Trainingsteilnahmen", + "members.title" to "Mitglieder", + "members.toggleSortDirection" to "Sortierrichtung wechseln", + "members.trainingGroups" to "Trainingsgruppen", + "members.trainingParticipations" to "Trainingsteilnahmen", + "members.transferErrorTitle" to "Fehler bei der Übertragung", + "members.transferMembers" to "Mitglieder übertragen", + "members.transferSuccessTitle" to "Übertragung erfolgreich", + "members.ttAdult" to "Erwachsene (kein Jugend nach Stichtag)", + "members.ttAgeClassCol" to "AK (TT)", + "members.ttFilterGroupJ" to "Mädchen & Jungen (gemischt)", + "members.ttFilterGroupM" to "Mädchen (nur weiblich)", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "aktuell", + "members.ttSeasonFilter" to "Saison (Stichtag)", + "members.ttSeasonNextTag" to "kommend", + "members.ttStichtagHint" to "Stichtag 1.1. (DTTB). Jungen: nur J-Klassen. Mädchen: J und M möglich.", + "members.updateRatings" to "TTR/QTTR von myTischtennis aktualisieren", + "members.updating" to "Aktualisiere...", + "members.visibleMembers" to "Sichtbare Mitglieder", + "members.wantsToPlay" to "Will spiele", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Abbreche", + "messages.confirm" to "Bestätige", + "messages.error" to "Fehler", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Erfolg", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Warnig", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Freigabe", + "navigation.backToHome" to "Zur Startseite", + "navigation.billing" to "Abrechnig", + "navigation.clickTtAccount" to "HTTV / click-TT-Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Vereinsiistellige", + "navigation.clubTournaments" to "Vereinsturniere", + "navigation.competitions" to "Wettbewerb", + "navigation.dailyBusiness" to "Tagesgschäft", + "navigation.diary" to "Tagebuech", + "navigation.home" to "Startseite", + "navigation.login" to "Aamelde", + "navigation.logout" to "Abmelde", + "navigation.logs" to "System-Logs", + "navigation.members" to "Mitglider", + "navigation.memberTransfer" to "Mitgliderübertragig", + "navigation.myTischtennisAccount" to "myTischtennis-Account", + "navigation.orders" to "Bestellige", + "navigation.permissions" to "Berechtigunge", + "navigation.personalSettings" to "Persönlichi Iistellige", + "navigation.predefinedActivities" to "Vordefinierte Aktivitäte", + "navigation.register" to "Registriere", + "navigation.schedule" to "Spielplän", + "navigation.settings" to "Iistellige", + "navigation.statistics" to "Trainings-Statistik", + "navigation.teamManagement" to "Team-Verwaltig", + "navigation.tournamentParticipations" to "Turnierteilnahmen", + "navigation.tournaments" to "Turnier", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Bestellig aalege", + "orders.budget" to "Budget", + "orders.club" to "Verein", + "orders.cost" to "Chöschte", + "orders.dateAutoHint" to "S Datum wird automatisch gsetzt und jedi Änderig mit em Datum protokolliert.", + "orders.errorLoading" to "Bestellige hend nöd chöne glade werde.", + "orders.errorSaving" to "Bestellig het nöd chöne gspeicheret werde.", + "orders.filterAllClubs" to "Alli Vereine", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Alli Status", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Da chasch alli Bestellige vereinsübergreifend aluege und verwalte.", + "orders.globalTitle" to "Bestellige aller Vereine", + "orders.history" to "Verlauf", + "orders.item" to "Was", + "orders.itemPlaceholder" to "z. B. Trikot, Hoodie oder Schlägerhülle", + "orders.loading" to "Bestellige werde glade...", + "orders.member" to "Mitgliid", + "orders.memberTitle" to "Bestellige: {name}", + "orders.noOrdersGlobal" to "Aktuell git's kei Bestellige.", + "orders.noOrdersMember" to "Für das Mitgliid git's no kei Bestellige.", + "orders.open" to "No offe", + "orders.orderDate" to "Erfasst am", + "orders.paid" to "Bezahlt", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Nach Verein, Mitgliid oder Artikel sueche", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "Artikel achoo", + "orders.statusDate" to "Letschti Änderig", + "orders.statusHandedOver" to "Artikel usghändigt", + "orders.statusOrdered" to "bstellt", + "orders.statusRequested" to "gwünscht", + "orders.title" to "Bestellige", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Verein:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Erstellt:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Spielklasse:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Meldung für:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Geplante Spielklasse:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Saison:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Team Altersklasse:", + "pdfGenerator.teamGenderLabel" to "Team Geschlecht:", + "pdfGenerator.teamLineupTitle" to "Mannschaftsaufstellung", + "pdfGenerator.teamNameLabel" to "Mannschaft:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Sproch", + "settings.languageChanged" to "Sproch erfolgriich gänderet", + "settings.languageDescription" to "Wähl dini bevorzugti Sproch für d Aawendig", + "settings.personalSettings" to "Persönlichi Iistellige", + "settings.selectLanguage" to "Sproch uswähle", + "settings.title" to "Iistellige", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planigs-Team hinzuefüege", + "teamManagement.allMembersAssigned" to "Alli Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zum Team hinzuefüege", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gspeicheret", + "teamManagement.autoSaveError" to "Automatischs Speichere isch fehlgschlage", + "teamManagement.availableLineupMembers" to "Verfügbare Spieler", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team aalege", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeite", + "teamManagement.eligibility" to "Einsatz", + "teamManagement.eligibilityAdultRelease" to "Freigabe Erwachsene", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Freigabe + Ersatz Erwachsene", + "teamManagement.eligibilityAdultReserve" to "Ersatz Erwachsene", + "teamManagement.eligibilityRegular" to "Regulär", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Aufstellung als PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüefe", + "teamManagement.filterNoLeague" to "Ohni Liga", + "teamManagement.firstHalf" to "Vorrunde", + "teamManagement.firstHalfFull" to "Vorrunde (Juli - Dezember)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Bitte eine Spielklasse auswählen und speichern.", + "teamManagement.lineupEmpty" to "Noch keine Spieler für diese Mannschaft gemeldet.", + "teamManagement.lineupPdfEmpty" to "Es sind keine Spieler in der Aufstellung – PDF kann nicht erstellt werden.", + "teamManagement.lineupPdfFilePrefix" to "Aufstellung", + "teamManagement.lineupProposal" to "Mannschaftsmeldung nach QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Mannschaftsmeldung konnte nicht gespeichert werden.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} hat mehr als 30 QTTR Punkte Vorsprung vor {lower}. Diese Reihenfolge bitte korrigieren.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markiere", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Fehlende Angaben:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "MyTischtennis-URL einfügen und parsen, um Team-ID und Ligendaten zu übernehmen.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Kei Spielklass", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Kei passende Mitglieder gfunde.", + "teamManagement.noPlannedLeague" to "Kei geplannti Spielklass", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Geplante Spielklasse", + "teamManagement.plannedLeagueHint" to "Optional. Unabhängig von der gemeldeten Liga (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "z. B. Bezirksliga, nach nächster Meldung …", + "teamManagement.planningSubtitle" to "Mitglieder uf geplannti Teams verteile, Riähefoug feschtlege und offeni Zuordnige aluege.", + "teamManagement.planningTitle" to "Mannschaftsplanig", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alli aktive Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wänd spiele", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied sueche", + "teamManagement.searchTeams" to "Team sueche", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Rückrunde", + "teamManagement.secondHalfFull" to "Rückrunde (ab 1. Januar)", + "teamManagement.selectedLineup" to "Gemeldete Spieler", + "teamManagement.selectMember" to "Mitglied uswähle", + "teamManagement.selectTeam" to "Team uswähle", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierig: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierig: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams uswähle, Konfiguration prüefe und Ligadate a eim Ort pflege.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltig", + "teamManagement.unassignedMembers" to "No nid zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohni Team-Zuordnig", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Pool-Regle hinzufüege", + "tournaments.address" to "Adrässe", + "tournaments.adjustTournamentSearch" to "Pass de Suechbegriff aa oder leg es neus Turnier aa.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Alli Teilnehmerdaten sind vollständig.", + "tournaments.allDataCompleteTop3" to "Alli Date vo de erschte 3 Plätz sind vollständig.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} Teilnehmendi bruuched e Uuswahl:", + "tournaments.atLeastOnePoolRule" to "Bitte mindestens e Pool-Regle für {label} aalege (z. B. Plätz 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} devo chönd automatisch zuegordnet werde.", + "tournaments.autoAssignEligible" to "Automatisch zuewise", + "tournaments.autoAssignEligibleDone" to "{count} Teilnehmendi sind automatisch zuegordnet worde.", + "tournaments.autoAssignEligibleNone" to "Aktuell git es kei Teilnehmendi mit einderütiger automatische Klassenzuewisig.", + "tournaments.autoAssignEligiblePartial" to "{successCount} Teilnehmendi sind automatisch zuegordnet worde, bi {errorCount} hets nid klappt.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Vorschläg:", + "tournaments.correctMatch" to "Korrigiere", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Endrunde us Zwüscherunde erstelle", + "tournaments.createFinalFromPreliminary" to "Endrunde us Vorrunde erstelle", + "tournaments.createGroupMatches" to "Gruppespiel berechne", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Zwüscherunde us Vorrunde erstelle", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Partner bilde", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "No nid erfasst", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Möchtsch d'Klass \"{name}\" würklich lösche?", + "tournaments.deleteClassParticipantsDetached" to "Alli Teilnehmer wärde vo dere Klass entfernt.", + "tournaments.deleteClassTitle" to "Klass lösche", + "tournaments.deleteExistingPairingsConfirm" to "Bestehendi Paarige wärde glöscht. Wiitermache?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Freii Tisch verteile", + "tournaments.distributeTablesResult" to "Tischverteilig", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} Teilnehmendi i dere Doppelklass hend no kei Partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alli vorhandene Klasse uf Doppel und legt neui Klasse standardmässig als Doppel aa.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Passt zu: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Bitte gib en Klassename ii.", + "tournaments.enterExternalParticipantName" to "Bitte gib mindestens Vorname und Nachname ii.", + "tournaments.enterMiniLocation" to "Bitte gib en Ort ii.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Fehler bim Verteile vo de Tisch.", + "tournaments.errorGeneratingPdf" to "Fehler bim Erstelle vom PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Endrunde", + "tournaments.finishMatch" to "Abschliesse", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "PDF wird erstellt...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Gruppe", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Zwüscherunde (Runde 2)", + "tournaments.internalStatsAbsoluteRank" to "Rangliste Gsamtwertig", + "tournaments.internalStatsAgeFilter" to "Altersklassä & Gschlächt (Einzel)", + "tournaments.internalStatsAgeFilterAll" to "Alli Altersklassä", + "tournaments.internalStatsAgeFilterNone" to "Kei Altersklass usgwählt", + "tournaments.internalStatsAgeNoClass" to "Ohni Klassäzuordnig", + "tournaments.internalStatsAgeSelectAll" to "Alli", + "tournaments.internalStatsAgeSelectNone" to "Keini", + "tournaments.internalStatsAverageRank" to "Rangliste Durchschnitt (pro Turnier)", + "tournaments.internalStatsAvgPoints" to "Ø", + "tournaments.internalStatsEmpty" to "Kei uswertbari Date im gwählte Ziitruum.", + "tournaments.internalStatsExportPdf" to "Als PDF exportieren", + "tournaments.internalStatsFilterAgeBands" to "Altersklassä", + "tournaments.internalStatsFilterGenderColumn" to "Gschlächt", + "tournaments.internalStatsGenderScopeAll" to "Alli", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Letscht 12 Mönet", + "tournaments.internalStatsLast3Months" to "Letscht 3 Mönet", + "tournaments.internalStatsLast6Months" to "Letscht 6 Mönet", + "tournaments.internalStatsOpenButton" to "Turnierstatistik (Einzel)", + "tournaments.internalStatsPeriod" to "Ziitruum", + "tournaments.internalStatsPoints" to "Summe", + "tournaments.internalStatsPointsExplain" to "Wertig: Pro Gruppe wird d Platzierig als Prozentzahl usdrückt (bi N Teilnehmer mit Platzierig: 1. = 100 %, Letschte = 0 %, dzwüsche linear; gliichi Platzierig = gliiche Wert). N umfasst alli Platzierte ide Gruppe (inkl. Gäscht). Bi nume einem Teilnehmer: 100 %. Wer d K.-o.-Rundi erreicht, bechunnt de höchschte Gruppewert vo de Chlass plus 1, deno no 1 Punkt pro gwunne K.-o.-Spiel. Nur Veräinsmitglieder (Einzel).", + "tournaments.internalStatsTitle" to "Statistik interne Turniere (Einzel)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} Turnier(e) im Ziitruum (ohni Minimeisterschafte).", + "tournaments.internalStatsTtAdult" to "Erwachsene", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Als laufend markiere", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Mindestens 2 Teilnehmer für Paarige nötig.", + "tournaments.missingDataPDF" to "Fehlendi Date als PDF", + "tournaments.missingDataPDFSubtitle" to "Bitte d'fehlende Date (markiert mit ____) bi de Teilnehmer nochfroge und do notiere.", + "tournaments.missingDataPDFSubtitleTop3" to "Fehlendi Date vo de erschte 3 (markiert mit ____) bitte nochfroge und do notiere.", + "tournaments.missingDataPDFTitle" to "Fehlendi Teilnehmerdaten – Minimeisterschaft", + "tournaments.missingDataPDFTitleTop3" to "Fehlendi Date – Top 3 Minimeisterschaft", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Neue Satz, z. B. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Kei Spiel verfüegbar, wo beidi Spieler frei sind.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "Kei passendi Klass gfunde.", + "tournaments.noFreeTables" to "Kei freie Tisch verfüegbar.", + "tournaments.noMatchingTournamentsTitle" to "Kei passendie Turnier", + "tournaments.noOrphanedMatchesFound" to "Kei verwaisti Spiel gfunde.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "No kei Regle. Biispil: Plätz 1 und 2 -> oberi Runde-2-Gruppe.", + "tournaments.noPoolRulesYetFinal" to "No kei Regle. Biispil: Plätz 1 und 2 -> Endrunde.", + "tournaments.noTop3Yet" to "D'erschte 3 Plätz stönd no nid fescht.", + "tournaments.noTournamentDate" to "Kei Turnierdatum vorhande.", + "tournaments.noTournamentsCreatedYet" to "Es sind no kei Turnier aagleit worde.", + "tournaments.noTrainingOnDate" to "Kei Trainingstag für {date} gfunde.", + "tournaments.noTrainingParticipants" to "Kei Teilnehmer im Training für das Datum gfunde.", + "tournaments.noValidTrainingParticipants" to "Kei gültige Teilnehmer im Training für das Datum gfunde.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Aazahl Tisch", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} verwaisti Spiel entfernt.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Siite", + "tournaments.pairingAlreadyExists" to "Die Paarig git's scho.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} Paarige erstellt, {errorCount} Fehler.", + "tournaments.participantConflicts" to "Konflikt", + "tournaments.participantNotFound" to "Teilnehmer nid gfunde.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmendi sind no keire Klass zuegordnet. Die sötted zerscht zueteilt werde.", + "tournaments.phone" to "Telefon", + "tournaments.placementsPendingFinals" to "Endplatzierige erschiined, sobald mehri Gruppe oder e Schlussrundi vorhande sind.", + "tournaments.placementsPendingGroups" to "Gruppeplatzierige erschiined, sobald Gruppespiel vorhande sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppespiel oder e Schlussrundi vorhande sind, erschiined d Platzierige da.", + "tournaments.placementsPendingSelectedClass" to "Für d aktuell usgwählti Klass git's no kei Platzierige.", + "tournaments.placementsPendingTitle" to "Platzierige no nöd verfügbar", + "tournaments.placesFromEachGroup" to "Plätz us jeder Gruppe (z. B. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Spieler 1", + "tournaments.playerTwo" to "Spieler 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Um Platz 3 spiele", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Regle {number}", + "tournaments.poolRulePlacesHelp" to "Bispiel: 1 oder 1,2", + "tournaments.poolRulePlacesLabel" to "Weli Plätz chömed wiiter?", + "tournaments.poolRulePreview" to "Us jedere Gruppe gönd Platz {places} wiiter i {target}.", + "tournaments.poolRuleQuickExamples" to "Schnällbispiel:", + "tournaments.poolRuleSummary" to "Plätz {places} gönd i {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} Zielgruppe", + "tournaments.poolRuleTargetKnockout" to "s K.-o.-Fäld", + "tournaments.poolRuleTargetLabel" to "Wohi gönd die Plätz?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Prüef Datum, Name und Gwünnsätz.", + "tournaments.problemConfigTitle" to "Konfiguration unvollständig", + "tournaments.problemConflictsDescription" to "Prüef unpassendi Klasse, fehlendi Date oder unklari Zuewisige.", + "tournaments.problemConflictsTitle" to "{count} Teilnehmendi mit Konflikt", + "tournaments.problemDoublesAutoDescription" to "D offeni Doppelklass cha direkt automatisch paarlet werde.", + "tournaments.problemDoublesDescription" to "Ei oder meh Doppelklasse bruuched no Partnerzuewisige.", + "tournaments.problemDoublesTitle" to "{count} offeni Doppelpartner", + "tournaments.problemGroupMatchesDescription" to "D Gruppe sind parat, aber d Spiel sind no nid erstellt.", + "tournaments.problemGroupMatchesTitle" to "Gruppespiel no nid erzeugt", + "tournaments.problemGroupsMissingDescription" to "Für s Gruppeturnier muesed zerscht Gruppe aagleit werde.", + "tournaments.problemGroupsMissingTitle" to "Gruppe no nid erstellt", + "tournaments.problemKnockoutReadyDescription" to "D Gruppenphase isch abgschlosse und d Endrundi cha jetzt erzeugt werde.", + "tournaments.problemKnockoutReadyTitle" to "K.-o.-Rundi cha gstartet werde", + "tournaments.problemUnassignedAutoDescription" to "{count} devo chönd direkt automatisch zuegordnet werde.", + "tournaments.problemUnassignedDescription" to "Die Teilnehmendi bruuched no e manuelli Klassenzuewisig.", + "tournaments.problemUnassignedTitle" to "{count} Teilnehmendi ohni Klass", + "tournaments.promotionRule12" to "Wiitercho: Vorrunde → Zwüscherunde (1→2)", + "tournaments.promotionRuleDescription12" to "Da legsch fescht, weli Plätz us jedere Vorrundegruppe i d Zwüscherundi chömed.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Da legsch fescht, weli Plätz us dr vorherige Rundi i d Gruppe vo dr Endrundi chömed.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Da legsch fescht, weli Plätz us dr vorherige Rundi is K.-o.-Fäld chömed.", + "tournaments.promotionRuleFinal" to "Wiitercho: Runde {from} → Runde {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Rangliste", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Aazahl Gruppe", + "tournaments.roundMode" to "Modus", + "tournaments.ruleStatusBlocked" to "Blockiert", + "tournaments.ruleStatusOk" to "Passt", + "tournaments.ruleStatusOkDescription" to "Diese Regel passt zur aktuellen Zielrunde und kann so verwendet werden.", + "tournaments.ruleStatusReview" to "Prüfen", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Runde speichere", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktivs Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Bitte wähl zerscht es Turnier us.", + "tournaments.selectTwoDifferentPlayers" to "Bitte wähl zwei verschiedeni Spieler us.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} vo {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Leg zerscht mindestens eini Witercho-Regle aa, bevor du Spieler i d nöchsti Rundi übernimmst.", + "tournaments.stageActionRun" to "Jetzt uusfüehre", + "tournaments.stageActionsDescription" to "Die Schritt übernähmed qualifizierti Spieler i d nöchsti Rundi.", + "tournaments.stageActionsTitle" to "Nächsti Aktione", + "tournaments.stageConfigBadServerResponse" to "Ungültigi Server-Antwort.", + "tournaments.stageConfigCurrentSetup" to "Aktuelli Struktur", + "tournaments.stageConfigIntro" to "D'Zwüscherunde isch optional. Wenn du sie aktiviersch, git's nacher immer e Endrunde. E KO-Endrunde wird als eis einzelnes Feld erzeugt.", + "tournaments.stageConfigLoadError" to "Fehler bim Lade vo de Rundenkonfiguration.", + "tournaments.stageConfigLoading" to "Lade Rundenkonfiguration …", + "tournaments.stageConfigMissingIds" to "Cha nid speichere: Vereins- oder Turnier-ID fählt.", + "tournaments.stageConfigTitle" to "Zwüscherunde & Endrunde", + "tournaments.stageCreated" to "Runde {round} isch erstellt worde.", + "tournaments.stageFinalDescription" to "Leg fescht, ob d Endrundi als Gruppe oder direkt als K.-o.-Fäld gspielt wird.", + "tournaments.stageFlowBreakdownActionAddRule" to "Regel anlegen", + "tournaments.stageFlowBreakdownActionOpen" to "Regel öffnen", + "tournaments.stageFlowBreakdownActionReview" to "Fehler prüfen", + "tournaments.stageFlowBreakdownErrors" to "{count} Fehler blockieren", + "tournaments.stageFlowBreakdownMissingRule" to "Noch keine vollständige Regel angelegt", + "tournaments.stageFlowBreakdownOk" to "Konfiguration passt", + "tournaments.stageFlowBreakdownWarnings" to "{count} Hinweis(e) offen", + "tournaments.stageFlowDirectFinal" to "Vorrundi -> Endrundi ({final})", + "tournaments.stageFlowHealthBlocked" to "Rundenlogik widersprüchlich", + "tournaments.stageFlowHealthBlockedDescription" to "Mindestens eine Regel passt aktuell nicht zur Zielrunde oder enthält blockierende Konfigurationsfehler.", + "tournaments.stageFlowHealthIncomplete" to "Rundenlogik unvollständig", + "tournaments.stageFlowHealthMissingRules" to "Mindestens ein Übergang hat noch keine vollständige Weiterkommens-Regel.", + "tournaments.stageFlowHealthOk" to "Rundenlogik passt", + "tournaments.stageFlowHealthOkDescription" to "Die Übergänge zwischen den Runden sind vollständig konfiguriert und aktuell stimmig.", + "tournaments.stageFlowHealthReviewDescription" to "Die Rundenlogik ist grundsätzlich nutzbar, sollte aber wegen offener Hinweise noch geprüft werden.", + "tournaments.stageFlowPreviewMissing" to "Noch keine Regel konfiguriert.", + "tournaments.stageFlowPreviewRule" to "Plätze {places} gehen in {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (voraussichtlich {count} Qualifizierte)", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Platz {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Aktuell qualifiziert", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} Gruppe(n) sind noch nicht vollständig entschieden", + "tournaments.stageFlowReadinessNoGroups" to "Noch keine passenden Gruppen vorhanden", + "tournaments.stageFlowReadinessReady" to "Übergang ist fachlich startklar", + "tournaments.stageFlowRecommendationError" to "Prüfe zuerst den ersten blockierenden Fehler in der Rundenlogik.", + "tournaments.stageFlowRecommendationFixes" to "Die typischen Konfigurationsprobleme können direkt automatisch bereinigt werden.", + "tournaments.stageFlowRecommendationMissingRule" to "Für {label} fehlt noch eine vollständige Regel. Lege am besten damit los.", + "tournaments.stageFlowRecommendationSave" to "Die Rundenlogik ist stimmig. Du kannst die Konfiguration jetzt speichern.", + "tournaments.stageFlowRecommendationTitle" to "Empfohlener nächster Schritt", + "tournaments.stageFlowRecommendationWarnings" to "Es gibt noch Hinweise, die du vor dem Speichern prüfen solltest.", + "tournaments.stageFlowWithIntermediate" to "Vorrundi -> Zwüscherundi ({stage2}) -> Endrundi ({final})", + "tournaments.stageParticipantsTransferred" to "Qualifizierti Spieler sind i {round} überno worde.", + "tournaments.stageStep1" to "Schritt 1", + "tournaments.stageStep1Description" to "Entscheid zerscht, ob nach dr Vorrundi no e zusätzlechi Rundi gspielt werde söll.", + "tournaments.stageStep1Title" to "Zwüscherundi festlege", + "tournaments.stageStep2" to "Schritt 2", + "tournaments.stageStep2Description" to "Leg fescht, wie d Zwüscherundi uusgseh söll und wie viu Gruppe sie brucht.", + "tournaments.stageStep3" to "Schritt 3", + "tournaments.stageValidationApplyAllFixes" to "{count} Schnellkorrektur(en) anwenden", + "tournaments.stageValidationApplyFix" to "Übernehmen", + "tournaments.stageValidationApplyTargetGroupFix" to "Auf {count} Zielgruppen setzen", + "tournaments.stageValidationApplyTargetTypeFix" to "Auf {target} umstellen", + "tournaments.stageValidationDescription" to "Diese Punkte solltest du vor dem Speichern oder Übernehmen der nächsten Runde korrigieren.", + "tournaments.stageValidationGroupCountRequired" to "Bitte mindestens eine gültige Gruppenanzahl festlegen.", + "tournaments.stageValidationKnockoutByesLikely" to "Mit voraussichtlich {count} Qualifizierten wird das K.-o.-Feld sehr wahrscheinlich Freilose enthalten.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "Für ein K.-o.-Feld werden mindestens 2 Qualifizierte benötigt.", + "tournaments.stageValidationKnockoutTargetOnly" to "Bei einer K.-o.-Endrunde ist hier nur das K.-o.-Feld als Ziel erlaubt.", + "tournaments.stageValidationNoRules" to "Für {stage} ist noch keine Weiterkommens-Regel angelegt.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "Mit nur {count} Qualifizierten für {groups} Zielgruppen würden leere Gruppen entstehen.", + "tournaments.stageValidationRulePlacesDuplicate" to "Eine Regel darf denselben Platz nicht mehrfach enthalten.", + "tournaments.stageValidationRulePlacesGap" to "Die Platzangabe hat Lücken. Meist ist eine zusammenhängende Reihenfolge wie 1,2 oder 1,2,3 gemeint.", + "tournaments.stageValidationRulePlacesInvalid" to "Die Platzangabe ist ungültig. Erlaubt sind nur positive ganze Zahlen wie 1 oder 1,2.", + "tournaments.stageValidationRulePlacesRequired" to "Trage mindestens einen Platz ein, der weiterkommen soll.", + "tournaments.stageValidationRulesOverlap" to "Platz {place} ist doppelt vergeben, nämlich in Regel {first} und Regel {second}.", + "tournaments.stageValidationSaveBlocked" to "Bitte zuerst die markierten Konfigurationsfehler korrigieren.", + "tournaments.stageValidationSuggestion" to "Vorschlag: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "Die Anzahl der Zielgruppen muss zur Zielrunde passen. Erwartet: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Bitte eine gültige Anzahl von Zielgruppen angeben.", + "tournaments.stageValidationTargetTypeMismatch" to "Das Ziel dieser Regel muss zur Zielrunde passen. Erwartet: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "Mit {count} Qualifizierten auf {groups} Zielgruppen werden die Gruppen vermutlich sehr klein.", + "tournaments.stageValidationTitle" to "Konfiguration prüfen", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuewise", + "tournaments.statusActionCreate" to "Erstelle", + "tournaments.statusActionGenerate" to "Erzüüge", + "tournaments.statusActionPair" to "Paar bilde", + "tournaments.statusActionReview" to "Prüefe", + "tournaments.statusActionStart" to "Starte", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration parat", + "tournaments.statusFinished" to "Fertig", + "tournaments.statusGroupsMissing" to "Gruppe no nid erstellt", + "tournaments.statusGroupsReady" to "Gruppe parat", + "tournaments.statusGroupsRunning" to "Gruppenphase lauft", + "tournaments.statusKnockoutReady" to "K.-o.-Rundi startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Rundi lauft", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Offe", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmendi mit Konflikt", + "tournaments.statusParticipantsReady" to "{count} Teilnehmendi ohni Konflikt", + "tournaments.statusParticipantsUnassigned" to "{count} ohni Klass", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} Doppel ohni Partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Tisch", + "tournaments.tablesDistributed" to "Tisch sind verteilt worde.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Ziel", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Ziel-Gruppenaazahl", + "tournaments.targetGroupsLabel" to "{count} Gruppe", + "tournaments.tournamentClassGenderFemale" to "Weiblich", + "tournaments.tournamentClassGenderOpen" to "Alli", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Qualifizierti Spieler i d Endrundi übernäh", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Brucht d Regle Zwüscherundi -> Endrundi und übernimmt d qualifizierti Spieler.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Qualifizierti Spieler direkt i d Endrundi übernäh", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Brucht d Regle Vorrundi -> Endrundi und erstellt det direkt d qualifizierti Spieler.", + "tournaments.transferQualifiedToIntermediate" to "Qualifizierti Spieler i d Zwüscherundi übernäh", + "tournaments.transferQualifiedToIntermediateDesc" to "Brucht d Regle Vorrundi -> Zwüscherundi und übernimmt d qualifizierti Spieler.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Laufend-Markierig entferne", + "tournaments.unpairedDoubles" to "Doppel ohni Partner", + "tournaments.useIntermediateStage" to "Zwüscherunde verwände", + "tournaments.warningBirthDateMissing" to "Geburtsdatum fehlt für die Klass", + "tournaments.warningClassMissing" to "Klass nid gfunde", + "tournaments.warningGenderMismatch" to "Gschlächt passt nid zur Klass", + "tournaments.warningGenderMissing" to "Gschlächt fehlt für die Klass", + "tournaments.warningMissingPairing" to "Doppelpartner fehlt", + "tournaments.warningTooOldForClass" to "Z'alt für die Klass", + "tournaments.warningTooYoungForClass" to "Z'jung für die Klass", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} offeni Pünkt", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Aktionen", + "trainingStats.activeMembers" to "Aktive Mitglieder", + "trainingStats.allTrainingDays" to "Alle Trainingstage", + "trainingStats.attendingMembers" to "Anwesende Mitglieder", + "trainingStats.averageParticipationCurrentMonth" to "Durchschnittliche Teilnahme (aktueller Monat)", + "trainingStats.averageParticipationHalfYear" to "Durchschnittliche Teilnahme (Halbjahr)", + "trainingStats.averageParticipationLastMonth" to "Durchschnittliche Teilnahme (letzter Monat)", + "trainingStats.averageParticipationQuarter" to "Durchschnittliche Teilnahme (Quartal)", + "trainingStats.averageParticipationYear" to "Durchschnittliche Teilnahme (Jahr)", + "trainingStats.birthdate" to "Geburtsdatum", + "trainingStats.date" to "Datum", + "trainingStats.lastTraining" to "Letztes Training", + "trainingStats.memberParticipations" to "Mitglieder-Teilnahmen", + "trainingStats.name" to "Name", + "trainingStats.noParticipants" to "Keine Teilnehmer", + "trainingStats.participants" to "Teilnehmer", + "trainingStats.participations12Months" to "Teilnahmen (12 Monate)", + "trainingStats.participations3Months" to "Teilnahmen (3 Monate)", + "trainingStats.participationsTotal" to "Teilnahmen (Gesamt)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Details anzeigen", + "trainingStats.title" to "Trainings-Statistik", + "trainingStats.trainingDayFilter" to "Trainingstag", + "trainingStats.trainingDays" to "Trainingstage (letzte 12 Monate)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Wochentag", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val de_extended: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Trainingstagebuch", + "app.title" to "Trainingstagebuch", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Aktivierung fehlgeschlagen. Bitte überprüfe den Link oder versuche es erneut.", + "auth.confirmPassword" to "Passwort bestätigen", + "auth.email" to "E-Mail", + "auth.forgotPassword" to "Passwort vergessen?", + "auth.forgotPasswordDescription" to "Gib deine E-Mail-Adresse ein. Du erhältst einen Link, um dein Passwort zurückzusetzen.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Einloggen", + "auth.loginFailed" to "Login fehlgeschlagen. Bitte Zugangsdaten prüfen und erneut versuchen.", + "auth.loginSuccess" to "Erfolgreich eingeloggt", + "auth.logout" to "Ausloggen", + "auth.logoutSuccess" to "Erfolgreich ausgeloggt", + "auth.newPassword" to "Neues Passwort", + "auth.noAccount" to "Noch kein Konto?", + "auth.password" to "Passwort", + "auth.passwordResetSuccess" to "Dein Passwort wurde erfolgreich geändert. Du kannst dich jetzt mit deinem neuen Passwort anmelden.", + "auth.passwordsDoNotMatch" to "Die Passwörter stimmen nicht überein.", + "auth.passwordTooShort" to "Das Passwort muss mindestens 6 Zeichen lang sein.", + "auth.register" to "Registrieren", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Angemeldet bleiben", + "auth.resetEmailSent" to "Falls ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet. Bitte prüfe dein Postfach (auch den Spam-Ordner).", + "auth.resetFailed" to "Passwort konnte nicht geändert werden. Der Link ist möglicherweise abgelaufen.", + "auth.resetPassword" to "Neues Passwort vergeben", + "auth.resetRequestFailed" to "Anfrage fehlgeschlagen. Bitte versuche es erneut.", + "auth.saveNewPassword" to "Passwort speichern", + "auth.saving" to "Wird gespeichert...", + "auth.sending" to "Wird gesendet...", + "auth.sendResetLink" to "Link senden", + "auth.sessionExpired" to "Deine Sitzung ist abgelaufen. Du wirst abgemeldet.", + "auth.toLogin" to "Zum Login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Zugriff auf den Verein nicht gestattet.", + "club.accessRequested" to "Zugriff wurde angefragt.", + "club.accessRequestFailed" to "Zugriffsanfrage konnte nicht gestellt werden.", + "club.accessRequestPending" to "Der Zugriff auf diesen Verein ist beantragt. Bitte haben Sie etwas Geduld.", + "club.create" to "Verein erstellen", + "club.createTitle" to "Verein anlegen", + "club.diary" to "Trainingstagebuch", + "club.errorLoadingRequests" to "Fehler beim Laden der offenen Anfragen", + "club.load" to "Laden", + "club.members" to "Mitglieder", + "club.mobileSelectHint" to "Bitte wähle zuerst einen Verein aus, um die App auf dem Smartphone zu nutzen.", + "club.name" to "Vereinsname", + "club.new" to "Neuer Verein", + "club.noAccess" to "Für diesen Verein wurde Dir noch kein Zugriff gestattet.", + "club.openAccessRequests" to "Offene Anfragen auf Zugriff", + "club.openRequests" to "Offene Anfragen auf Zugriff", + "club.requestAccess" to "Zugriff beantragen", + "club.select" to "Verein auswählen", + "club.selectPlaceholder" to "Verein wählen...", + "club.title" to "Verein", + "club.trainingDiary" to "Trainingstagebuch", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Aktionen", + "common.active" to "Aktiv", + "common.add" to "Hinzufügen", + "common.all" to "Alle", + "common.apply" to "Anwenden", + "common.back" to "Zurück", + "common.cancel" to "Abbrechen", + "common.choose" to "Wählen", + "common.clear" to "Löschen", + "common.close" to "Schließen", + "common.confirm" to "Bestätigen", + "common.create" to "Erstellen", + "common.date" to "Datum", + "common.days" to "Tage", + "common.delete" to "Löschen", + "common.description" to "Beschreibung", + "common.details" to "Details", + "common.disabled" to "Deaktiviert", + "common.download" to "herunterladen", + "common.edit" to "Bearbeiten", + "common.enabled" to "Aktiviert", + "common.filter" to "Filter", + "common.hours" to "Stunden", + "common.in" to "in", + "common.inactive" to "Inaktiv", + "common.loading" to "Lade...", + "common.min" to "Min", + "common.minutes" to "Minuten", + "common.months" to "Monate", + "common.move" to "Verschieben", + "common.name" to "Name", + "common.new" to "Neu", + "common.next" to "Weiter", + "common.no" to "Nein", + "common.ok" to "OK", + "common.optional" to "Optional", + "common.period" to "Zeitraum", + "common.previous" to "Zurück", + "common.refresh" to "Neu laden", + "common.remove" to "Entfernen", + "common.required" to "Erforderlich", + "common.reset" to "Zurücksetzen", + "common.save" to "Speichern", + "common.saved" to "Gespeichert", + "common.saving" to "Speichere...", + "common.search" to "Suchen", + "common.select" to "Auswählen", + "common.status" to "Status", + "common.submit" to "Absenden", + "common.time" to "Zeit", + "common.today" to "Heute", + "common.type" to "Typ", + "common.update" to "Aktualisieren", + "common.view" to "Anzeigen", + "common.weeks" to "Wochen", + "common.years" to "Jahre", + "common.yes" to "Ja", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Schlag hinzufügen", + "courtDrawingTool.backhand" to "Rückhand", + "courtDrawingTool.codeLabel" to "Kürzel", + "courtDrawingTool.completeFirstStroke" to "1. Schlag vervollständigen", + "courtDrawingTool.completeFirstStrokeHint" to "Wähle Startposition, Schlagseite, Rotation und Ziel. Danach erscheint die grafische Darstellung.", + "courtDrawingTool.configureExercise" to "Übung konfigurieren", + "courtDrawingTool.counterSpin" to "Gegenläufer", + "courtDrawingTool.forehand" to "Vorhand", + "courtDrawingTool.noAdditionalStrokes" to "Noch keine Folgeschläge angelegt.", + "courtDrawingTool.preview" to "Vorschau", + "courtDrawingTool.previewHint" to "Die Grafik erscheint, sobald der erste Schlag vollständig gesetzt ist.", + "courtDrawingTool.service" to "Aufschlag:", + "courtDrawingTool.serviceTitle" to "Aufschlag", + "courtDrawingTool.sidespin" to "Seitschnitt", + "courtDrawingTool.sideUnderspin" to "Seitunterschnitt", + "courtDrawingTool.spin" to "Schnitt:", + "courtDrawingTool.startLeft" to "links", + "courtDrawingTool.startMiddle" to "mitte", + "courtDrawingTool.startRight" to "rechts", + "courtDrawingTool.stepAdditionalStrokes" to "4. Folgeschläge", + "courtDrawingTool.stepAdditionalStrokesHint" to "Optional weitere Bälle als Liste aufbauen.", + "courtDrawingTool.stepFirstStroke" to "2. 1. Schlag", + "courtDrawingTool.stepFirstStrokeHint" to "Schlagseite und Rotation für den ersten Ball festlegen.", + "courtDrawingTool.stepStartPosition" to "1. Startposition", + "courtDrawingTool.stepStartPositionHint" to "Aus welcher Aufschlagposition startet die Übung?", + "courtDrawingTool.stepTarget" to "3. Ziel", + "courtDrawingTool.stepTargetHint" to "Zielzone für den ersten Schlag auswählen.", + "courtDrawingTool.strokeSide" to "Seite", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Schnittabwehr", + "courtDrawingTool.strokeTypeCounter" to "Konter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Schlagart", + "courtDrawingTool.strokeTypeLobDefense" to "Ballonabwehr", + "courtDrawingTool.strokeTypePush" to "Schupf", + "courtDrawingTool.strokeTypeSmash" to "Schuss", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Rückhand halblang", + "courtDrawingTool.targetBackhandLong" to "Rückhand lang", + "courtDrawingTool.targetBackhandShort" to "Rückhand kurz", + "courtDrawingTool.targetForehandHalfLong" to "Vorhand halblang", + "courtDrawingTool.targetForehandLong" to "Vorhand lang", + "courtDrawingTool.targetForehandShort" to "Vorhand kurz", + "courtDrawingTool.targetMiddleHalfLong" to "Mitte halblang", + "courtDrawingTool.targetMiddleLong" to "Mitte lang", + "courtDrawingTool.targetMiddleShort" to "Mitte kurz", + "courtDrawingTool.targetPosition" to "Zielposition:", + "courtDrawingTool.targetPositionLabel" to "Zielposition", + "courtDrawingTool.title" to "Tischtennis-Übungszeichnung", + "courtDrawingTool.titleLabel" to "Titel", + "courtDrawingTool.topspin" to "Überschnitt", + "courtDrawingTool.toTarget" to "nach", + "courtDrawingTool.underspin" to "Unterschnitt", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Aktiver Trainingstag", + "diary.activities" to "Aktivitäten", + "diary.activity" to "Aktivität", + "diary.activityDrawing" to "Aktivitätszeichnung", + "diary.activityImage" to "Aktivitätsbild", + "diary.activityNotFound" to "Aktivität nicht gefunden. Bitte wählen Sie eine aus der Liste aus.", + "diary.activityOrTimeblock" to "Aktivität / Zeitblock", + "diary.activityPlaceholder" to "Aktivität", + "diary.activityRequired" to "Bitte geben Sie eine Aktivität ein.", + "diary.addActivity" to "Aktivität hinzufügen", + "diary.addGroup" to "Gruppe hinzufügen", + "diary.addGroupActivity" to "Gruppen-Aktivität hinzufügen", + "diary.addGroupButton" to "+ Gruppe", + "diary.addTimeblock" to "Zeitblock", + "diary.all" to "Alle", + "diary.applySuggestion" to "Vorschlag übernehmen", + "diary.assignParticipants" to "Teilnehmer zuordnen", + "diary.assignParticipantsForGroupActivity" to "Teilnehmer für Gruppen-Aktivität zuordnen", + "diary.assignShort" to "Zuordnen", + "diary.bookAccident" to "Unfall buchen", + "diary.confirmDelete" to "Löschen bestätigen", + "diary.confirmDeleteDate" to "Möchten Sie dieses Datum wirklich löschen?", + "diary.confirmDeleteDateDetails" to "Alle zugehörigen Daten werden ebenfalls gelöscht.", + "diary.confirmDeleteGroup" to "Möchten Sie die Gruppe \"{name}\" wirklich löschen?", + "diary.createDate" to "Datum anlegen", + "diary.createDrawing" to "Übungszeichnung erstellen", + "diary.createGroups" to "Gruppen erstellen", + "diary.createNew" to "Neu anlegen", + "diary.createNewDate" to "Neues Datum anlegen", + "diary.date" to "Datum", + "diary.dateCannotBeDeleted" to "Datum kann nicht gelöscht werden", + "diary.dateCannotBeDeletedDetails" to "Es sind noch Inhalte vorhanden (Trainingplan, Teilnehmer, Aktivitäten, Unfälle oder Notizen).", + "diary.dateNoLongerCurrent" to "Ausgewähltes Datum war nicht mehr aktuell. Bitte erneut versuchen.", + "diary.delete" to "Löschen", + "diary.deleteDate" to "Datum löschen", + "diary.deleteGroup" to "Gruppe löschen", + "diary.duration" to "Dauer", + "diary.durationExampleLong" to "z.B. 2x7 oder 3*5", + "diary.durationExampleShort" to "z.B. 2x7", + "diary.durationMinutes" to "Dauer (Min)", + "diary.editActivity" to "Aktivität bearbeiten", + "diary.editGroupActivity" to "Gruppen-Aktivität bearbeiten", + "diary.editTrainingTimes" to "Trainingszeiten bearbeiten", + "diary.errorCreatingActivity" to "Fehler beim Erstellen der Aktivität", + "diary.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen", + "diary.errorDeletingGroup" to "Fehler beim Löschen der Gruppe", + "diary.errorLoadingPredefinedActivities" to "Fehler beim Laden der vordefinierten Aktivitäten", + "diary.errorMarkingForm" to "Fehler beim Markieren des Mitgliedsformulars", + "diary.errorOccurred" to "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "diary.existingGroups" to "Vorhandene Gruppen", + "diary.filterAbsent" to "Abwesend", + "diary.filterAll" to "Alle", + "diary.filterExcused" to "Entschuldigt", + "diary.filterPresent" to "Anwesend", + "diary.filterTest" to "Probe", + "diary.formHandedOver" to "Mitgliedsformular ausgehändigt", + "diary.formMarkedAsHandedOver" to "Mitgliedsformular als ausgehändigt markiert", + "diary.freeActivities" to "Freie Aktivitäten", + "diary.gallery" to "Mitglieder-Galerie", + "diary.galleryCreating" to "Galerie wird erstellt…", + "diary.group" to "Gruppe", + "diary.groupDeletedSuccessfully" to "Gruppe wurde erfolgreich gelöscht!", + "diary.groupManagement" to "Gruppenverwaltung", + "diary.groupsCreated" to "{count} Gruppen wurden erfolgreich erstellt!", + "diary.groupsLabel" to "Gruppen", + "diary.groupsSection" to "Gruppen", + "diary.leader" to "Leiter", + "diary.min" to "Min", + "diary.minutes" to "Minuten", + "diary.mustCreateAtLeastTwoGroups" to "Beim ersten Erstellen müssen mindestens 2 Gruppen erstellt werden!", + "diary.nextAppointment" to "Nächster Termin", + "diary.noActiveTrainingDay" to "Kein Trainingstag ausgewählt.", + "diary.noEntries" to "Keine Einträge", + "diary.noFreeActivitiesYet" to "Noch keine freien Aktivitäten erfasst.", + "diary.noParticipants" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "diary.numberOfGroups" to "Anzahl Gruppen", + "diary.oneGroupAdded" to "1 Gruppe wurde erfolgreich hinzugefügt!", + "diary.openPlanItems" to "{count} offen", + "diary.openPlanItemsLabel" to "Plan-Status", + "diary.overallActivity" to "Gesamt-Aktivität", + "diary.participants" to "Teilnehmer", + "diary.participantStatusCancelled" to "Abgesagt", + "diary.participantStatusExcused" to "Entschuldigt", + "diary.participantStatusNone" to "Kein Status", + "diary.planActivitiesCount" to "Plan-Aktivitäten", + "diary.planAddHint" to "Neue Plan-Elemente fügst du über die Aktionen oben hinzu.", + "diary.planEmptyState" to "Im Trainingsplan ist noch nichts eingetragen.", + "diary.quickAdd" to "+ Schnell hinzufügen", + "diary.searchParticipants" to "Teilnehmer suchen", + "diary.selectGroup" to "Gruppe auswählen...", + "diary.selectGroupAndActivity" to "Bitte wählen Sie eine Gruppe und geben Sie eine Aktivität ein.", + "diary.selectParticipantAndNote" to "Bitte wählen Sie einen Teilnehmer aus und geben Sie einen Notiztext ein.", + "diary.selectTags" to "Tags auswählen", + "diary.selectTrainingGroup" to "Trainingsgruppe auswählen", + "diary.selectTrainingGroupPlaceholder" to "Bitte wählen...", + "diary.showImage" to "Bild/Zeichnung anzeigen", + "diary.skipSuggestion" to "Ohne Vorschlag fortfahren", + "diary.standardActivities" to "Standard-Aktivitäten", + "diary.standardActivityAddError" to "Standard-Aktivität konnte nicht hinzugefügt werden.", + "diary.standardDurationShort" to "Min", + "diary.startTime" to "Startzeit", + "diary.statusEmpty" to "Dieser Trainingstag ist noch leer.", + "diary.statusInProgress" to "Dieser Trainingstag ist teilweise vorbereitet.", + "diary.statusOpenShort" to "Offen", + "diary.statusReady" to "Zeiten und Trainingsplan sind gepflegt.", + "diary.statusReadyShort" to "Bereit", + "diary.suggestion" to "Vorschlag", + "diary.timeblock" to "Zeitblock", + "diary.timeblocksCount" to "Zeitblöcke", + "diary.title" to "Trainingstagebuch", + "diary.today" to "Heute", + "diary.trainingDayAsPDF" to "Trainingstag als PDF herunterladen", + "diary.trainingDayAsPDFShort" to "Trainingstag als PDF", + "diary.trainingDayChecklist" to "Abschlusscheck", + "diary.trainingDaySection" to "Trainingstag", + "diary.trainingDaySummaryPdfShort" to "Teilnehmerübersicht als PDF", + "diary.trainingEnd" to "Trainingsende", + "diary.trainingPlan" to "Trainingsplan", + "diary.trainingPlanAsPDF" to "Trainingsplan als PDF", + "diary.trainingPlanPdfShort" to "Ablaufplan als PDF", + "diary.trainingStart" to "Trainingsbeginn", + "diary.trainingTimesUpdated" to "Trainingszeiten erfolgreich aktualisiert.", + "diary.trainingWindow" to "Trainingsfenster", + "diary.trainingWindowUnset" to "Noch nicht gesetzt", + "diary.unassignedPlanItems" to "{count} offen", + "diary.updateTimes" to "Zeiten aktualisieren", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.features.myTischtennis" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertungen", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management & Ligen", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.tournaments" to "Turniere (intern, offen, offiziell)", + "home.features.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.features.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch", + "languages.de-CH" to "Schweizer Deutsch", + "languages.en-AU" to "Englisch (AU)", + "languages.en-GB" to "Englisch (GB)", + "languages.en-US" to "Englisch (US)", + "languages.es" to "Spanisch", + "languages.fil" to "Filipino", + "languages.fr" to "Französisch", + "languages.it" to "Italienisch", + "languages.ja" to "Japanisch", + "languages.pl" to "Polnisch", + "languages.th" to "Thai", + "languages.tl" to "Tagalog", + "languages.zh" to "Chinesisch", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "logs.viewDetails" to "Details", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Aktionen", + "members.active" to "Aktiv", + "members.activeMembers" to "Aktive Mitglieder", + "members.addEmail" to "E-Mail-Adresse hinzufügen", + "members.addGroup" to "Gruppe hinzufügen...", + "members.addPhone" to "Telefonnummer hinzufügen", + "members.address" to "Adresse", + "members.adultReleaseApproved" to "Freigabe Erwachsene", + "members.adultReserveApproved" to "Ersatz bei Erwachsenen", + "members.adults" to "Erwachsene (18+)", + "members.age" to "Alter", + "members.ageFromPlaceholder" to "von", + "members.ageGroup" to "Altersklasse", + "members.ageRange" to "Alter von - bis", + "members.ageToPlaceholder" to "bis", + "members.batchFormsMarkedEmpty" to "In der aktuellen Auswahl gibt es keine ungeprüften Formulare.", + "members.batchFormsMarkedSuccess" to "{count} Formular(e) als geprüft markiert.", + "members.batchMarkedRegularEmpty" to "In der aktuellen Auswahl gibt es keine Probe-Mitglieder.", + "members.batchMarkedRegularSuccess" to "{count} Probe-Mitglied(er) als regulär markiert.", + "members.batchPartialFailure" to "{success} erfolgreich, {failed} fehlgeschlagen.", + "members.birthdate" to "Geburtsdatum", + "members.bulkActions" to "Sammelaktionen", + "members.camera" to "Kamera", + "members.change" to "Ändern", + "members.city" to "Ort", + "members.clearFields" to "Felder leeren", + "members.clearFilters" to "Filter zurücksetzen", + "members.clickTtRequestAction" to "Spielberechtigung in click-TT beantragen", + "members.clickTtRequestConfirm" to "Soll für {name} der automatisierte Click-TT-Antrag gestartet werden?", + "members.clickTtRequestError" to "Der Click-TT-Antrag konnte nicht eingereicht werden.", + "members.clickTtRequestFailedLog" to "Click-TT-Antrag fehlgeschlagen", + "members.clickTtRequestHint" to "Der Antrag wird im Backend automatisiert durch den Click-TT-Workflow geführt.", + "members.clickTtRequestPending" to "Click-TT-Antrag läuft", + "members.clickTtRequestPendingShort" to "Läuft ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Bitte loggen Sie sich noch auf click-tt ein und senden Sie den Antrag ab.", + "members.clickTtRequestTitle" to "Click-TT-Antrag starten", + "members.clickTtSubmitted" to "Click-TT gestellt", + "members.closeEditor" to "Editor schließen", + "members.composeEmail" to "E-Mail vorbereiten", + "members.contact" to "Kontakt", + "members.copyContactSummary" to "Kontaktübersicht kopieren", + "members.copyContactSummarySuccess" to "Kontaktübersicht in die Zwischenablage kopiert.", + "members.copyEmails" to "E-Mails kopieren", + "members.copyEmailsEmpty" to "In der aktuellen Auswahl gibt es keine E-Mail-Adressen.", + "members.copyEmailsSuccess" to "E-Mail-Liste in die Zwischenablage kopiert.", + "members.copyPhones" to "Telefone kopieren", + "members.copyPhonesEmpty" to "In der aktuellen Auswahl gibt es keine Telefonnummern.", + "members.copyPhonesSuccess" to "Telefonliste in die Zwischenablage kopiert.", + "members.create" to "Anlegen", + "members.createNewMember" to "Neues Mitglied anlegen", + "members.dataIssueAddress" to "Adresse fehlt", + "members.dataIssueBirthdate" to "Geburtsdatum fehlt", + "members.dataIssueCity" to "Ort fehlt", + "members.dataIssueEmail" to "E-Mail fehlt", + "members.dataIssueGender" to "Geschlecht ungeklärt", + "members.dataIssuePhone" to "Telefon fehlt", + "members.dataIssuePostalCode" to "PLZ fehlt", + "members.dataIssueStreet" to "Straße fehlt", + "members.dataIssueTrainingGroup" to "Trainingsgruppe fehlt", + "members.dataQuality" to "Datenqualität", + "members.dataQualityComplete" to "Daten vollständig", + "members.deactivateMember" to "Mitglied deaktivieren", + "members.deactivateMemberConfirm" to "Möchten Sie \"{name}\" wirklich deaktivieren?", + "members.deactivateMemberTitle" to "Mitglied deaktivieren", + "members.deleteImageConfirm" to "Möchten Sie dieses Bild wirklich löschen?", + "members.deleteImageTitle" to "Bild löschen", + "members.editHint" to "Ein Klick auf eine Zeile öffnet den Editor.", + "members.editMember" to "Mitglied bearbeiten", + "members.editorAssignTrainingGroupHint" to "Bitte mindestens eine Trainingsgruppe zuordnen.", + "members.editorCreateHint" to "Neues Mitglied anlegen und Kontaktdaten direkt erfassen.", + "members.editorEditHint" to "Daten von {name} bearbeiten.", + "members.editorRecommendedEntry" to "Empfohlener Eintrag", + "members.emailAddress" to "E-Mail-Adresse", + "members.emailAddressShort" to "Email-Adresse", + "members.emails" to "E-Mail-Adressen", + "members.errorAddingToGroup" to "Fehler beim Hinzufügen zur Gruppe", + "members.errorDeactivatingMember" to "Fehler beim Deaktivieren des Mitglieds", + "members.errorDeletingImage" to "Bild konnte nicht gelöscht werden", + "members.errorLoadingImage" to "Fehler beim Laden des Bildes", + "members.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "members.errorLoadingTrainingParticipations" to "Fehler beim Laden der Trainingsteilnahmen", + "members.errorMarkingForm" to "Fehler beim Markieren des Formulars.", + "members.errorRemovingFromGroup" to "Fehler beim Entfernen aus der Gruppe", + "members.errorRemovingTestMembership" to "Fehler beim Entfernen der Testmitgliedschaft.", + "members.errorRotatingImage" to "Fehler beim Drehen des Bildes", + "members.errorSavingMember" to "Fehler beim Speichern des Mitglieds", + "members.errorSettingPrimaryImage" to "Hauptbild konnte nicht gesetzt werden", + "members.errorTransfer" to "Fehler bei der Übertragung", + "members.errorUpdatingRatings" to "Fehler beim Aktualisieren der TTR/QTTR-Werte", + "members.errorUploadingImage" to "Bild konnte nicht hochgeladen werden", + "members.exercises" to "Übungen", + "members.exportCsv" to "CSV exportieren", + "members.exportCsvFile" to "mitglieder-auswahl.csv", + "members.exportEmails" to "E-Mail", + "members.exportMembersSelected" to "Mitglieder aktuell ausgewählt", + "members.exportPhones" to "Telefon", + "members.exportPreview" to "Exportvorschau", + "members.exportPreviewEmpty" to "Keine Mitglieder in der aktuellen Auswahl", + "members.exportPreviewNames" to "Vorschau", + "members.exportReachableByEmail" to "mit E-Mail-Adresse", + "members.exportReachableByPhone" to "mit Telefonnummer", + "members.firstName" to "Vorname", + "members.formHandedOver" to "Mitgliedsformular ausgehändigt", + "members.formMarkedAsHandedOver" to "Mitgliedsformular als ausgehändigt markiert.", + "members.gender" to "Geschlecht", + "members.genderDiverse" to "Divers", + "members.genderFemale" to "Weiblich", + "members.genderMale" to "Männlich", + "members.genderUnknown" to "Unbekannt", + "members.generatePhoneList" to "Telefonliste generieren", + "members.groupPhotoCrop" to "Gruppenfoto verarbeiten", + "members.groupPhotoCropSaved" to "Mitgliedsfoto aus Gruppenfoto gespeichert.", + "members.image" to "Bild", + "members.imageDeleted" to "Bild wurde gelöscht.", + "members.imageInternet" to "Bild (Inet?)", + "members.imagePreview" to "Vorschau des Mitgliedsbildes", + "members.imageUpdated" to "Bild wurde aktualisiert", + "members.inactive" to "inaktiv", + "members.inactiveMembers" to "Inaktive Mitglieder", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17 (17 und jünger)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Nachname", + "members.lastTrainingFilter" to "Letztes Training", + "members.lastTrainingFilterHasDate" to "Mit erfasstem Datum", + "members.lastTrainingFilterHint" to "AK-Spalte: laufende und nächste Saison. Weitere Details (letztes Training, Teilnahmen) auch beim Überfahren der Zeile.", + "members.lastTrainingFilterNoDate" to "Ohne letztes Training", + "members.lastTrainingFilterNotInTraining" to "„Nicht mehr im Training“", + "members.loadingMembers" to "Lade Mitglieder...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Formular geprüft", + "members.markFormsForSelection" to "Formulare für Auswahl prüfen", + "members.markRegular" to "Als regulär markieren", + "members.markRegularForSelection" to "Auswahl als regulär markieren", + "members.memberDeactivated" to "Mitglied deaktiviert", + "members.memberDetails" to "Mitgliedsdetails", + "members.memberFormHandedOver" to "Mitgliedsformular ausgehändigt", + "members.memberImage" to "Mitgliedsbild", + "members.memberImages" to "Mitgliedsbilder", + "members.memberInfo" to "Mitglieder-Info", + "members.memberScope" to "Mitgliedsbereich", + "members.missingTtrHistoryId" to "Keine myTischtennis-ID hinterlegt", + "members.name" to "Name, Vorname", + "members.newMember" to "Neues Mitglied", + "members.noAddressShort" to "Keine Adresse", + "members.noEmailShort" to "Keine E-Mail", + "members.noGroupsAssigned" to "Keine Gruppen zugeordnet", + "members.noGroupsAvailable" to "Keine Gruppen verfügbar", + "members.noOpenTasks" to "Keine offenen Aufgaben", + "members.noPhoneShort" to "Kein Telefon", + "members.notes" to "Notizen", + "members.noTestMembership" to "Keine Testmitgliedschaft mehr", + "members.notInTrainingTooltip" to "Seit {weeks} Trainingswochen ohne Teilnahme", + "members.onlyActiveMembers" to "Es werden nur aktive Mitglieder ausgegeben", + "members.openTasks" to "Offene Aufgaben", + "members.parent" to "Elternteil", + "members.parentFallback" to "Elternteil", + "members.parentName" to "Name (z.B. Mutter, Vater)", + "members.phoneList" to "Telefonliste.pdf", + "members.phoneListForSelection" to "Telefonliste für Auswahl", + "members.phoneListSelectionFile" to "Telefonliste-Auswahl.pdf", + "members.phoneNumber" to "Telefonnummer", + "members.phoneNumberShort" to "Telefon-Nr.", + "members.phones" to "Telefonnummern", + "members.picsInInternetAllowed" to "Pics in Internet erlaubt", + "members.postalCode" to "PLZ", + "members.previewLastTraining" to "Letztes Training", + "members.previewNoLastTraining" to "Noch keine Teilnahme", + "members.primary" to "Primär", + "members.primaryImageUpdated" to "Hauptbild wurde aktualisiert.", + "members.remove" to "Entfernen", + "members.resultsVisible" to "Mitglieder sichtbar", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Aktiv", + "members.scopeActiveDataIncomplete" to "Aktiv + Daten unvollständig", + "members.scopeActiveTest" to "Aktiv + Probe", + "members.scopeAll" to "Alle", + "members.scopeDataIncomplete" to "Daten unvollständig", + "members.scopeInactive" to "Inaktiv", + "members.scopeNeedsForm" to "Formular ungeprüft", + "members.scopeNotTraining" to "Nicht mehr im Training", + "members.scopeTest" to "Probe", + "members.search" to "Suche", + "members.searchAndFilter" to "Suche und Filter", + "members.searchPlaceholder" to "Name, Ort, Telefon oder E-Mail suchen", + "members.selectFile" to "Datei auswählen", + "members.showInactiveMembers" to "Inaktive Mitglieder anzeigen", + "members.showTrainingParticipationsColumn" to "Spalte „Trainingsteilnahmen“ anzeigen", + "members.showTtrHistory" to "TTR-Historie anzeigen", + "members.sixOrMoreParticipations" to "6 oder mehr Trainingsteilnahmen", + "members.sortAge" to "Alter", + "members.sortBirthday" to "Geburtstag", + "members.sortBy" to "Sortieren nach", + "members.sortFirstName" to "Vorname", + "members.sortLastName" to "Nachname", + "members.sortLastTraining" to "Letztes Training", + "members.sortOpenTasks" to "Offene Aufgaben", + "members.sortQttr" to "QTTR-Wert", + "members.status" to "Status", + "members.street" to "Straße", + "members.subtitle" to "Mitglieder suchen, filtern und direkt bearbeiten.", + "members.taskActionMarkRegular" to "Regulär setzen", + "members.taskActionRequest" to "Anfragen", + "members.taskActionReview" to "Öffnen", + "members.taskActionVerify" to "Prüfen", + "members.taskAssignTrainingGroup" to "Trainingsgruppe zuordnen", + "members.taskCheckClickTt" to "Click-TT-Spielberechtigung prüfen", + "members.taskCheckDataQuality" to "Datenqualität prüfen", + "members.taskCheckTrainingStatus" to "Trainingsstatus prüfen", + "members.taskReviewTrialStatus" to "Probe-Status prüfen", + "members.taskVerifyForm" to "Formular prüfen", + "members.testMember" to "Testm.", + "members.testMembers" to "Testmitglieder", + "members.testMembership" to "Testmitgliedschaft", + "members.testMembershipRemoved" to "Testmitgliedschaft entfernt.", + "members.threeOrMoreParticipations" to "3 oder mehr Trainingsteilnahmen", + "members.title" to "Mitglieder", + "members.toggleSortDirection" to "Sortierrichtung wechseln", + "members.trainingGroups" to "Trainingsgruppen", + "members.trainingParticipations" to "Trainingsteilnahmen", + "members.transferErrorTitle" to "Fehler bei der Übertragung", + "members.transferMembers" to "Mitglieder übertragen", + "members.transferSuccessTitle" to "Übertragung erfolgreich", + "members.ttAdult" to "Erwachsene (kein Jugend nach Stichtag)", + "members.ttAgeClassCol" to "AK (TT)", + "members.ttFilterGroupJ" to "Mädchen & Jungen (gemischt)", + "members.ttFilterGroupM" to "Mädchen (nur weiblich)", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "aktuell", + "members.ttSeasonFilter" to "Saison (Stichtag)", + "members.ttSeasonNextTag" to "kommend", + "members.ttStichtagHint" to "Stichtag 1.1. (DTTB). Jungen: nur J-Klassen. Mädchen: J und M möglich.", + "members.updateRatings" to "TTR/QTTR von myTischtennis aktualisieren", + "members.updating" to "Aktualisiere...", + "members.visibleMembers" to "Sichtbare Mitglieder", + "members.wantsToPlay" to "Will spielen", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung", + "memberTransfer.transferConfig" to "Übertragungskonfiguration", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Abbrechen", + "messages.confirm" to "Bestätigen", + "messages.error" to "Fehler", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Erfolg", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Warnung", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Freigaben", + "navigation.backToHome" to "Zur Startseite", + "navigation.billing" to "Abrechnung", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Vereinseinstellungen", + "navigation.clubTournaments" to "Vereinsturniere", + "navigation.competitions" to "Wettbewerbe", + "navigation.dailyBusiness" to "Tagesgeschäft", + "navigation.diary" to "Tagebuch", + "navigation.home" to "Startseite", + "navigation.login" to "Einloggen", + "navigation.logout" to "Ausloggen", + "navigation.logs" to "System-Logs", + "navigation.members" to "Mitglieder", + "navigation.memberTransfer" to "Mitgliederübertragung", + "navigation.myTischtennisAccount" to "myTischtennis-Account", + "navigation.orders" to "Bestellungen", + "navigation.permissions" to "Berechtigungen", + "navigation.personalSettings" to "Persönliche Einstellungen", + "navigation.predefinedActivities" to "Vordefinierte Aktivitäten", + "navigation.register" to "Registrieren", + "navigation.schedule" to "Spielpläne", + "navigation.settings" to "Einstellungen", + "navigation.statistics" to "Trainings-Statistik", + "navigation.teamManagement" to "Team-Verwaltung", + "navigation.tournamentParticipations" to "Turnierteilnahmen", + "navigation.tournaments" to "Turniere", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Bestellung anlegen", + "orders.budget" to "Budget", + "orders.club" to "Verein", + "orders.cost" to "Kosten", + "orders.dateAutoHint" to "Datum wird automatisch gesetzt und jede Änderung mit Datum protokolliert.", + "orders.errorLoading" to "Bestellungen konnten nicht geladen werden.", + "orders.errorSaving" to "Bestellung konnte nicht gespeichert werden.", + "orders.filterAllClubs" to "Alle Vereine", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Alle Status", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Hier werden alle Bestellungen vereinsübergreifend angezeigt und verwaltet.", + "orders.globalTitle" to "Bestellungen aller Vereine", + "orders.history" to "Verlauf", + "orders.item" to "Was", + "orders.itemPlaceholder" to "z. B. Trikot, Hoodie oder Schlägerhülle", + "orders.loading" to "Lade Bestellungen...", + "orders.member" to "Mitglied", + "orders.memberTitle" to "Bestellungen: {name}", + "orders.noOrdersGlobal" to "Es gibt aktuell keine Bestellungen.", + "orders.noOrdersMember" to "Für dieses Mitglied gibt es noch keine Bestellungen.", + "orders.open" to "Noch offen", + "orders.orderDate" to "Erfasst am", + "orders.paid" to "Bezahlt", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Nach Verein, Mitglied oder Artikel suchen", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "Artikel angekommen", + "orders.statusDate" to "Letzte Änderung", + "orders.statusHandedOver" to "Artikel ausgehändigt", + "orders.statusOrdered" to "bestellt", + "orders.statusRequested" to "gewünscht", + "orders.title" to "Bestellungen", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Verein:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Erstellt:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Spielklasse:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Meldung für:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Geplante Spielklasse:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Saison:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Team Altersklasse:", + "pdfGenerator.teamGenderLabel" to "Team Geschlecht:", + "pdfGenerator.teamLineupTitle" to "Mannschaftsaufstellung", + "pdfGenerator.teamNameLabel" to "Mannschaft:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.close" to "Abbrechen", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.resetResource" to "Zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Sprache", + "settings.languageChanged" to "Sprache erfolgreich geändert", + "settings.languageDescription" to "Wähle deine bevorzugte Sprache für die Anwendung", + "settings.personalSettings" to "Persönliche Einstellungen", + "settings.selectLanguage" to "Sprache auswählen", + "settings.title" to "Einstellungen", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Verfügbare Spieler", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Einsatz", + "teamManagement.eligibilityAdultRelease" to "Freigabe Erwachsene", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Freigabe + Ersatz Erwachsene", + "teamManagement.eligibilityAdultReserve" to "Ersatz Erwachsene", + "teamManagement.eligibilityRegular" to "Regulär", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Aufstellung als PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Vorrunde", + "teamManagement.firstHalfFull" to "Vorrunde (Juli - Dezember)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Bitte eine Spielklasse auswählen und speichern.", + "teamManagement.lineupEmpty" to "Noch keine Spieler für diese Mannschaft gemeldet.", + "teamManagement.lineupPdfEmpty" to "Es sind keine Spieler in der Aufstellung – PDF kann nicht erstellt werden.", + "teamManagement.lineupPdfFilePrefix" to "Aufstellung", + "teamManagement.lineupProposal" to "Mannschaftsmeldung nach QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Mannschaftsmeldung konnte nicht gespeichert werden.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} hat mehr als 30 QTTR Punkte Vorsprung vor {lower}. Diese Reihenfolge bitte korrigieren.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Fehlende Angaben:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "MyTischtennis-URL einfügen und parsen, um Team-ID und Ligendaten zu übernehmen.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Geplante Spielklasse", + "teamManagement.plannedLeagueHint" to "Optional. Unabhängig von der gemeldeten Liga (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "z. B. Bezirksliga, nach nächster Meldung …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Rückrunde", + "teamManagement.secondHalfFull" to "Rückrunde (ab 1. Januar)", + "teamManagement.selectedLineup" to "Gemeldete Spieler", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Pool-Regel hinzufügen", + "tournaments.address" to "Adresse", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Alle Teilnehmerdaten sind vollständig.", + "tournaments.allDataCompleteTop3" to "Alle Daten der Top-3-Platzierten sind vollständig.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} Teilnehmer brauchen eine Auswahl:", + "tournaments.atLeastOnePoolRule" to "Bitte mindestens eine Pool-Regel für {label} anlegen (z. B. Plätze 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} davon können automatisch zugeordnet werden.", + "tournaments.autoAssignEligible" to "Automatisch zuweisen", + "tournaments.autoAssignEligibleDone" to "{count} Teilnehmer wurden automatisch zugeordnet.", + "tournaments.autoAssignEligibleNone" to "Aktuell gibt es keine Teilnehmer mit eindeutiger automatischer Klassenzuweisung.", + "tournaments.autoAssignEligiblePartial" to "{successCount} Teilnehmer wurden automatisch zugeordnet, {errorCount} nicht.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Vorschläge:", + "tournaments.correctMatch" to "Korrigieren", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Endrunde aus Zwischenrunde erstellen", + "tournaments.createFinalFromPreliminary" to "Endrunde aus Vorrunde erstellen", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Zwischenrunde aus Vorrunde erstellen", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Partner bilden", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Noch nicht erfasst", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Möchten Sie die Klasse \"{name}\" wirklich löschen?", + "tournaments.deleteClassParticipantsDetached" to "Alle Teilnehmer werden von dieser Klasse entfernt.", + "tournaments.deleteClassTitle" to "Klasse löschen", + "tournaments.deleteExistingPairingsConfirm" to "Bestehende Paarungen werden gelöscht. Fortfahren?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Freie Tische verteilen", + "tournaments.distributeTablesResult" to "Tischverteilung", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} Teilnehmer in dieser Doppelklasse haben noch keinen Partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Passt zu: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Bitte geben Sie einen Klassennamen ein.", + "tournaments.enterExternalParticipantName" to "Bitte geben Sie mindestens Vorname und Nachname ein.", + "tournaments.enterMiniLocation" to "Bitte geben Sie einen Ort ein.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Fehler beim Verteilen der Tische.", + "tournaments.errorGeneratingPdf" to "Fehler beim Generieren des PDFs.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.events" to "Veranstaltungen", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Endrunde", + "tournaments.finishMatch" to "Abschließen", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "PDF wird erstellt...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Gruppen", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Zwischenrunde (Runde 2)", + "tournaments.internalStatsAbsoluteRank" to "Rangliste Gesamtwertung", + "tournaments.internalStatsAgeFilter" to "Altersklassen & Geschlecht (Einzel)", + "tournaments.internalStatsAgeFilterAll" to "Alle Altersklassen", + "tournaments.internalStatsAgeFilterNone" to "Keine Altersklasse ausgewählt", + "tournaments.internalStatsAgeNoClass" to "Ohne Klassenzuordnung", + "tournaments.internalStatsAgeSelectAll" to "Alle", + "tournaments.internalStatsAgeSelectNone" to "Keine", + "tournaments.internalStatsAverageRank" to "Rangliste Durchschnitt (pro Turnier)", + "tournaments.internalStatsAvgPoints" to "Ø", + "tournaments.internalStatsEmpty" to "Keine auswertbaren Daten im gewählten Zeitraum.", + "tournaments.internalStatsExportPdf" to "Als PDF exportieren", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Letzte 12 Monate", + "tournaments.internalStatsLast3Months" to "Letzte 3 Monate", + "tournaments.internalStatsLast6Months" to "Letzte 6 Monate", + "tournaments.internalStatsOpenButton" to "Turnierstatistik (Einzel)", + "tournaments.internalStatsPeriod" to "Zeitraum", + "tournaments.internalStatsPoints" to "Summe", + "tournaments.internalStatsPointsExplain" to "Wertung: Pro Gruppe wird die Platzierung als Prozentzahl ausgedrückt (bei N Teilnehmern mit Platzierung: 1. = 100 %, Letzter = 0 %, dazwischen linear; gleiche Platzierung = gleicher Wert). N umfasst alle Platzierten in der Gruppe (inkl. Gäste). Bei nur einem Teilnehmer: 100 %. Wer die K.-o.-Runde erreicht, erhält den höchsten Gruppenwert der Klasse plus 1, danach je gewonnenes K.-o.-Spiel einen weiteren Punkt. Nur Vereinsmitglieder (Einzel).", + "tournaments.internalStatsTitle" to "Statistik interne Turniere (Einzel)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} Turnier(e) im Zeitraum (ohne Minimeisterschaften).", + "tournaments.internalStatsTtAdult" to "Erwachsene", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Als laufend markieren", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Mindestens 2 Teilnehmer für Paarungen erforderlich.", + "tournaments.missingDataPDF" to "Fehlende Daten als PDF", + "tournaments.missingDataPDFSubtitle" to "Bitte die fehlenden Daten (markiert mit ____) bei den Teilnehmern erfragen und hier notieren.", + "tournaments.missingDataPDFSubtitleTop3" to "Fehlende Daten der ersten 3 Plätze (markiert mit ____) bitte erfragen und hier notieren.", + "tournaments.missingDataPDFTitle" to "Fehlende Teilnehmerdaten – Minimeisterschaft", + "tournaments.missingDataPDFTitleTop3" to "Fehlende Daten – Top 3 Minimeisterschaft", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Neuen Satz, z. B. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Keine Spiele verfügbar, bei denen beide Spieler frei sind.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "Keine passende Klasse gefunden.", + "tournaments.noFreeTables" to "Keine freien Tische verfügbar.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Keine verwaisten Spiele gefunden.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Noch keine Regeln. Beispiel: Plätze 1 und 2 -> obere Runde-2-Gruppen.", + "tournaments.noPoolRulesYetFinal" to "Noch keine Regeln. Beispiel: Plätze 1 und 2 -> Endrunde.", + "tournaments.noTop3Yet" to "Es stehen noch keine Top-3-Platzierungen fest.", + "tournaments.noTournamentDate" to "Kein Turnierdatum vorhanden.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Kein Trainingstag für {date} gefunden.", + "tournaments.noTrainingParticipants" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden.", + "tournaments.noValidTrainingParticipants" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Anzahl Tische", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} verwaiste Spiele entfernt.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Seite", + "tournaments.pairingAlreadyExists" to "Diese Paarung existiert bereits.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} Paarungen erstellt, {errorCount} Fehler.", + "tournaments.participantConflicts" to "Konflikte", + "tournaments.participantNotFound" to "Teilnehmer nicht gefunden.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.participations" to "Turnierbeteiligungen", + "tournaments.phone" to "Telefon", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Plätze aus jeder Gruppe (z. B. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Spieler 1", + "tournaments.playerTwo" to "Spieler 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Platz 3 ausspielen", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Regel {number}", + "tournaments.poolRulePlacesHelp" to "Beispiel: 1 oder 1,2", + "tournaments.poolRulePlacesLabel" to "Welche Plätze kommen weiter?", + "tournaments.poolRulePreview" to "Aus jeder Gruppe gehen Platz {places} weiter in {target}.", + "tournaments.poolRuleQuickExamples" to "Schnellbeispiele:", + "tournaments.poolRuleSummary" to "Plätze {places} gehen in {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} Zielgruppen", + "tournaments.poolRuleTargetKnockout" to "das K.-o.-Feld", + "tournaments.poolRuleTargetLabel" to "Wohin gehen diese Plätze?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Prüfe Datum, Name und Gewinnsätze.", + "tournaments.problemConfigTitle" to "Konfiguration unvollständig", + "tournaments.problemConflictsDescription" to "Prüfe unpassende Klassen, fehlende Daten oder unklare Zuordnungen.", + "tournaments.problemConflictsTitle" to "{count} Teilnehmer mit Konflikten", + "tournaments.problemDoublesAutoDescription" to "Die offene Doppelklasse kann direkt automatisch gepaart werden.", + "tournaments.problemDoublesDescription" to "Ein oder mehrere Doppelklassen brauchen noch Partnerzuordnungen.", + "tournaments.problemDoublesTitle" to "{count} offene Doppelpartner", + "tournaments.problemGroupMatchesDescription" to "Die Gruppen sind bereit, aber die Spiele wurden noch nicht erstellt.", + "tournaments.problemGroupMatchesTitle" to "Gruppenspiele noch nicht erzeugt", + "tournaments.problemGroupsMissingDescription" to "Für das Gruppenturnier müssen zuerst Gruppen angelegt werden.", + "tournaments.problemGroupsMissingTitle" to "Gruppen noch nicht erstellt", + "tournaments.problemKnockoutReadyDescription" to "Die Gruppenphase ist abgeschlossen und die Endrunde kann jetzt erzeugt werden.", + "tournaments.problemKnockoutReadyTitle" to "K.-o.-Runde kann gestartet werden", + "tournaments.problemUnassignedAutoDescription" to "{count} davon können direkt automatisch zugeordnet werden.", + "tournaments.problemUnassignedDescription" to "Diese Teilnehmer brauchen noch eine manuelle Klassenzuordnung.", + "tournaments.problemUnassignedTitle" to "{count} Teilnehmer ohne Klasse", + "tournaments.promotionRule12" to "Weiterkommen: Vorrunde → Zwischenrunde (1→2)", + "tournaments.promotionRuleDescription12" to "Hier legst du fest, welche Plätze aus jeder Vorrundengruppe in die Zwischenrunde kommen.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Hier legst du fest, welche Plätze aus der vorherigen Runde in die Gruppen der Endrunde kommen.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Hier legst du fest, welche Plätze aus der vorherigen Runde in das K.-o.-Feld kommen.", + "tournaments.promotionRuleFinal" to "Weiterkommen: Runde {from} → Runde {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Rangliste", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Anzahl Gruppen", + "tournaments.roundMode" to "Modus", + "tournaments.ruleStatusBlocked" to "Blockiert", + "tournaments.ruleStatusOk" to "Passt", + "tournaments.ruleStatusOkDescription" to "Diese Regel passt zur aktuellen Zielrunde und kann so verwendet werden.", + "tournaments.ruleStatusReview" to "Prüfen", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Runden speichern", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Bitte wählen Sie zuerst ein Turnier aus.", + "tournaments.selectTwoDifferentPlayers" to "Bitte wählen Sie zwei verschiedene Spieler aus.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Lege zuerst mindestens eine Weiterkommens-Regel an, bevor du Spieler in die nächste Runde übernimmst.", + "tournaments.stageActionRun" to "Jetzt ausführen", + "tournaments.stageActionsDescription" to "Diese Schritte übernehmen qualifizierte Spieler in die nächste Runde.", + "tournaments.stageActionsTitle" to "Nächste Aktionen", + "tournaments.stageConfigBadServerResponse" to "Fehlerhafte Antwort vom Server.", + "tournaments.stageConfigCurrentSetup" to "Aktuelle Struktur", + "tournaments.stageConfigIntro" to "Zwischenrunde ist optional. Wenn du sie aktivierst, gibt es danach immer eine Endrunde. KO-Endrunde wird als ein einziges Feld erzeugt.", + "tournaments.stageConfigLoadError" to "Fehler beim Laden der Rundenkonfiguration.", + "tournaments.stageConfigLoading" to "Lade Zwischenrunden …", + "tournaments.stageConfigMissingIds" to "Kann nicht speichern: Vereins- oder Turnier-ID fehlt.", + "tournaments.stageConfigTitle" to "Zwischenrunde & Endrunde", + "tournaments.stageCreated" to "Runde {round} wurde erstellt.", + "tournaments.stageFinalDescription" to "Bestimme, ob die Endrunde als Gruppenphase oder direkt als K.-o.-Feld gespielt wird.", + "tournaments.stageFlowBreakdownActionAddRule" to "Regel anlegen", + "tournaments.stageFlowBreakdownActionOpen" to "Regel öffnen", + "tournaments.stageFlowBreakdownActionReview" to "Fehler prüfen", + "tournaments.stageFlowBreakdownErrors" to "{count} Fehler blockieren", + "tournaments.stageFlowBreakdownMissingRule" to "Noch keine vollständige Regel angelegt", + "tournaments.stageFlowBreakdownOk" to "Konfiguration passt", + "tournaments.stageFlowBreakdownWarnings" to "{count} Hinweis(e) offen", + "tournaments.stageFlowDirectFinal" to "Vorrunde -> Endrunde ({final})", + "tournaments.stageFlowHealthBlocked" to "Rundenlogik widersprüchlich", + "tournaments.stageFlowHealthBlockedDescription" to "Mindestens eine Regel passt aktuell nicht zur Zielrunde oder enthält blockierende Konfigurationsfehler.", + "tournaments.stageFlowHealthIncomplete" to "Rundenlogik unvollständig", + "tournaments.stageFlowHealthMissingRules" to "Mindestens ein Übergang hat noch keine vollständige Weiterkommens-Regel.", + "tournaments.stageFlowHealthOk" to "Rundenlogik passt", + "tournaments.stageFlowHealthOkDescription" to "Die Übergänge zwischen den Runden sind vollständig konfiguriert und aktuell stimmig.", + "tournaments.stageFlowHealthReviewDescription" to "Die Rundenlogik ist grundsätzlich nutzbar, sollte aber wegen offener Hinweise noch geprüft werden.", + "tournaments.stageFlowPreviewMissing" to "Noch keine Regel konfiguriert.", + "tournaments.stageFlowPreviewRule" to "Plätze {places} gehen in {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (voraussichtlich {count} Qualifizierte)", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Platz {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Aktuell qualifiziert", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} Gruppe(n) sind noch nicht vollständig entschieden", + "tournaments.stageFlowReadinessNoGroups" to "Noch keine passenden Gruppen vorhanden", + "tournaments.stageFlowReadinessReady" to "Übergang ist fachlich startklar", + "tournaments.stageFlowRecommendationError" to "Prüfe zuerst den ersten blockierenden Fehler in der Rundenlogik.", + "tournaments.stageFlowRecommendationFixes" to "Die typischen Konfigurationsprobleme können direkt automatisch bereinigt werden.", + "tournaments.stageFlowRecommendationMissingRule" to "Für {label} fehlt noch eine vollständige Regel. Lege am besten damit los.", + "tournaments.stageFlowRecommendationSave" to "Die Rundenlogik ist stimmig. Du kannst die Konfiguration jetzt speichern.", + "tournaments.stageFlowRecommendationTitle" to "Empfohlener nächster Schritt", + "tournaments.stageFlowRecommendationWarnings" to "Es gibt noch Hinweise, die du vor dem Speichern prüfen solltest.", + "tournaments.stageFlowWithIntermediate" to "Vorrunde -> Zwischenrunde ({stage2}) -> Endrunde ({final})", + "tournaments.stageParticipantsTransferred" to "Qualifizierte Spieler wurden in {round} übernommen.", + "tournaments.stageStep1" to "Schritt 1", + "tournaments.stageStep1Description" to "Entscheide zuerst, ob nach der Vorrunde noch eine zusätzliche Runde gespielt werden soll.", + "tournaments.stageStep1Title" to "Zwischenrunde festlegen", + "tournaments.stageStep2" to "Schritt 2", + "tournaments.stageStep2Description" to "Lege fest, wie die Zwischenrunde aussieht und wie viele Gruppen dort gebraucht werden.", + "tournaments.stageStep3" to "Schritt 3", + "tournaments.stageValidationApplyAllFixes" to "{count} Schnellkorrektur(en) anwenden", + "tournaments.stageValidationApplyFix" to "Übernehmen", + "tournaments.stageValidationApplyTargetGroupFix" to "Auf {count} Zielgruppen setzen", + "tournaments.stageValidationApplyTargetTypeFix" to "Auf {target} umstellen", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "Mit voraussichtlich {count} Qualifizierten wird das K.-o.-Feld sehr wahrscheinlich Freilose enthalten.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "Für ein K.-o.-Feld werden mindestens 2 Qualifizierte benötigt.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "Mit nur {count} Qualifizierten für {groups} Zielgruppen würden leere Gruppen entstehen.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "Die Platzangabe hat Lücken. Meist ist eine zusammenhängende Reihenfolge wie 1,2 oder 1,2,3 gemeint.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Vorschlag: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "Die Anzahl der Zielgruppen muss zur Zielrunde passen. Erwartet: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "Das Ziel dieser Regel muss zur Zielrunde passen. Erwartet: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "Mit {count} Qualifizierten auf {groups} Zielgruppen werden die Gruppen vermutlich sehr klein.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Paaren", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Fertig", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Offen", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} Doppel ohne Partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Tisch", + "tournaments.tablesDistributed" to "Tische wurden verteilt.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Ziel", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Ziel-Gruppenanzahl", + "tournaments.targetGroupsLabel" to "{count} Gruppen", + "tournaments.title" to "Turniere", + "tournaments.tournamentClassGenderFemale" to "Weiblich", + "tournaments.tournamentClassGenderOpen" to "Alle", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Qualifizierte Spieler in die Endrunde übernehmen", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Nutzt die Regeln Zwischenrunde -> Endrunde und übernimmt die qualifizierten Teilnehmer.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Qualifizierte Spieler direkt in die Endrunde übernehmen", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Nutzt die Regeln Vorrunde -> Endrunde und erzeugt daraus direkt die qualifizierten Teilnehmer.", + "tournaments.transferQualifiedToIntermediate" to "Qualifizierte Spieler in die Zwischenrunde übernehmen", + "tournaments.transferQualifiedToIntermediateDesc" to "Nutzt die Regeln Vorrunde -> Zwischenrunde und übernimmt die qualifizierten Teilnehmer.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Laufend-Markierung entfernen", + "tournaments.unpairedDoubles" to "Doppel ohne Partner", + "tournaments.useIntermediateStage" to "Zwischenrunde verwenden", + "tournaments.warningBirthDateMissing" to "Geburtsdatum fehlt für diese Klasse", + "tournaments.warningClassMissing" to "Klasse nicht gefunden", + "tournaments.warningGenderMismatch" to "Geschlecht passt nicht zur Klasse", + "tournaments.warningGenderMissing" to "Geschlecht fehlt für diese Klasse", + "tournaments.warningMissingPairing" to "Doppelpartner fehlt", + "tournaments.warningTooOldForClass" to "Zu alt für diese Klasse", + "tournaments.warningTooYoungForClass" to "Zu jung für diese Klasse", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} offene Punkte", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Aktionen", + "trainingStats.activeMembers" to "Aktive Mitglieder", + "trainingStats.allTrainingDays" to "Alle Trainingstage", + "trainingStats.attendingMembers" to "Anwesende Mitglieder", + "trainingStats.averageParticipationCurrentMonth" to "Durchschnittliche Teilnahme (aktueller Monat)", + "trainingStats.averageParticipationHalfYear" to "Durchschnittliche Teilnahme (Halbjahr)", + "trainingStats.averageParticipationLastMonth" to "Durchschnittliche Teilnahme (letzter Monat)", + "trainingStats.averageParticipationQuarter" to "Durchschnittliche Teilnahme (Quartal)", + "trainingStats.averageParticipationYear" to "Durchschnittliche Teilnahme (Jahr)", + "trainingStats.birthdate" to "Geburtsdatum", + "trainingStats.date" to "Datum", + "trainingStats.lastTraining" to "Letztes Training", + "trainingStats.memberParticipations" to "Mitglieder-Teilnahmen", + "trainingStats.name" to "Name", + "trainingStats.noParticipants" to "Keine Teilnehmer", + "trainingStats.participants" to "Teilnehmer", + "trainingStats.participations12Months" to "Teilnahmen (12 Monate)", + "trainingStats.participations3Months" to "Teilnahmen (3 Monate)", + "trainingStats.participationsTotal" to "Teilnahmen (Gesamt)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Details anzeigen", + "trainingStats.title" to "Trainings-Statistik", + "trainingStats.trainingDayFilter" to "Trainingstag", + "trainingStats.trainingDays" to "Trainingstage (letzte 12 Monate)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Wochentag", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val de: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Trainingstagebuch", + "app.title" to "Trainingstagebuch", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Aktivierung fehlgeschlagen. Bitte überprüfe den Link oder versuche es erneut.", + "auth.confirmPassword" to "Passwort bestätigen", + "auth.email" to "E-Mail", + "auth.forgotPassword" to "Passwort vergessen?", + "auth.forgotPasswordDescription" to "Gib deine E-Mail-Adresse ein. Du erhältst einen Link, um dein Passwort zurückzusetzen.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Einloggen", + "auth.loginFailed" to "Login fehlgeschlagen. Bitte Zugangsdaten prüfen und erneut versuchen.", + "auth.loginSuccess" to "Erfolgreich eingeloggt", + "auth.logout" to "Ausloggen", + "auth.logoutSuccess" to "Erfolgreich ausgeloggt", + "auth.newPassword" to "Neues Passwort", + "auth.noAccount" to "Noch kein Konto?", + "auth.password" to "Passwort", + "auth.passwordResetSuccess" to "Dein Passwort wurde erfolgreich geändert. Du kannst dich jetzt mit deinem neuen Passwort anmelden.", + "auth.passwordsDoNotMatch" to "Die Passwörter stimmen nicht überein.", + "auth.passwordTooShort" to "Das Passwort muss mindestens 6 Zeichen lang sein.", + "auth.register" to "Registrieren", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Angemeldet bleiben", + "auth.resetEmailSent" to "Falls ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet. Bitte prüfe dein Postfach (auch den Spam-Ordner).", + "auth.resetFailed" to "Passwort konnte nicht geändert werden. Der Link ist möglicherweise abgelaufen.", + "auth.resetPassword" to "Neues Passwort vergeben", + "auth.resetRequestFailed" to "Anfrage fehlgeschlagen. Bitte versuche es erneut.", + "auth.saveNewPassword" to "Passwort speichern", + "auth.saving" to "Wird gespeichert...", + "auth.sending" to "Wird gesendet...", + "auth.sendResetLink" to "Link senden", + "auth.sessionExpired" to "Deine Sitzung ist abgelaufen. Du wirst abgemeldet.", + "auth.toLogin" to "Zum Login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Zugriff auf den Verein nicht gestattet.", + "club.accessRequested" to "Zugriff wurde angefragt.", + "club.accessRequestFailed" to "Zugriffsanfrage konnte nicht gestellt werden.", + "club.accessRequestPending" to "Der Zugriff auf diesen Verein ist beantragt. Bitte haben Sie etwas Geduld.", + "club.create" to "Verein erstellen", + "club.createTitle" to "Verein anlegen", + "club.diary" to "Trainingstagebuch", + "club.errorLoadingRequests" to "Fehler beim Laden der offenen Anfragen", + "club.load" to "Laden", + "club.members" to "Mitglieder", + "club.mobileSelectHint" to "Bitte wähle zuerst einen Verein aus, um die App auf dem Smartphone zu nutzen.", + "club.name" to "Vereinsname", + "club.new" to "Neuer Verein", + "club.noAccess" to "Für diesen Verein wurde Dir noch kein Zugriff gestattet.", + "club.openAccessRequests" to "Offene Anfragen auf Zugriff", + "club.openRequests" to "Offene Anfragen auf Zugriff", + "club.requestAccess" to "Zugriff beantragen", + "club.select" to "Verein auswählen", + "club.selectPlaceholder" to "Verein wählen...", + "club.title" to "Verein", + "club.trainingDiary" to "Trainingstagebuch", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Aktionen", + "common.active" to "Aktiv", + "common.add" to "Hinzufügen", + "common.all" to "Alle", + "common.apply" to "Anwenden", + "common.back" to "Zurück", + "common.cancel" to "Abbrechen", + "common.choose" to "Wählen", + "common.clear" to "Löschen", + "common.close" to "Schließen", + "common.confirm" to "Bestätigen", + "common.create" to "Erstellen", + "common.date" to "Datum", + "common.days" to "Tage", + "common.delete" to "Löschen", + "common.description" to "Beschreibung", + "common.details" to "Details", + "common.disabled" to "Deaktiviert", + "common.download" to "herunterladen", + "common.edit" to "Bearbeiten", + "common.enabled" to "Aktiviert", + "common.filter" to "Filter", + "common.hours" to "Stunden", + "common.in" to "in", + "common.inactive" to "Inaktiv", + "common.loading" to "Lade...", + "common.min" to "Min", + "common.minutes" to "Minuten", + "common.months" to "Monate", + "common.move" to "Verschieben", + "common.name" to "Name", + "common.new" to "Neu", + "common.next" to "Weiter", + "common.no" to "Nein", + "common.ok" to "OK", + "common.optional" to "Optional", + "common.period" to "Zeitraum", + "common.previous" to "Zurück", + "common.refresh" to "Neu laden", + "common.remove" to "Entfernen", + "common.required" to "Erforderlich", + "common.reset" to "Zurücksetzen", + "common.save" to "Speichern", + "common.saved" to "Gespeichert", + "common.saving" to "Speichere...", + "common.search" to "Suchen", + "common.select" to "Auswählen", + "common.status" to "Status", + "common.submit" to "Absenden", + "common.time" to "Zeit", + "common.today" to "Heute", + "common.type" to "Typ", + "common.update" to "Aktualisieren", + "common.view" to "Anzeigen", + "common.weeks" to "Wochen", + "common.years" to "Jahre", + "common.yes" to "Ja", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Schlag hinzufügen", + "courtDrawingTool.backhand" to "Rückhand", + "courtDrawingTool.codeLabel" to "Kürzel", + "courtDrawingTool.completeFirstStroke" to "1. Schlag vervollständigen", + "courtDrawingTool.completeFirstStrokeHint" to "Wähle Startposition, Schlagseite, Rotation und Ziel. Danach erscheint die grafische Darstellung.", + "courtDrawingTool.configureExercise" to "Übung konfigurieren", + "courtDrawingTool.counterSpin" to "Gegenläufer", + "courtDrawingTool.forehand" to "Vorhand", + "courtDrawingTool.noAdditionalStrokes" to "Noch keine Folgeschläge angelegt.", + "courtDrawingTool.preview" to "Vorschau", + "courtDrawingTool.previewHint" to "Die Grafik erscheint, sobald der erste Schlag vollständig gesetzt ist.", + "courtDrawingTool.service" to "Aufschlag:", + "courtDrawingTool.serviceTitle" to "Aufschlag", + "courtDrawingTool.sidespin" to "Seitschnitt", + "courtDrawingTool.sideUnderspin" to "Seitunterschnitt", + "courtDrawingTool.spin" to "Schnitt:", + "courtDrawingTool.startLeft" to "links", + "courtDrawingTool.startMiddle" to "mitte", + "courtDrawingTool.startRight" to "rechts", + "courtDrawingTool.stepAdditionalStrokes" to "4. Folgeschläge", + "courtDrawingTool.stepAdditionalStrokesHint" to "Optional weitere Bälle als Liste aufbauen.", + "courtDrawingTool.stepFirstStroke" to "2. 1. Schlag", + "courtDrawingTool.stepFirstStrokeHint" to "Schlagseite und Rotation für den ersten Ball festlegen.", + "courtDrawingTool.stepStartPosition" to "1. Startposition", + "courtDrawingTool.stepStartPositionHint" to "Aus welcher Aufschlagposition startet die Übung?", + "courtDrawingTool.stepTarget" to "3. Ziel", + "courtDrawingTool.stepTargetHint" to "Zielzone für den ersten Schlag auswählen.", + "courtDrawingTool.strokeSide" to "Seite", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Schnittabwehr", + "courtDrawingTool.strokeTypeCounter" to "Konter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Schlagart", + "courtDrawingTool.strokeTypeLobDefense" to "Ballonabwehr", + "courtDrawingTool.strokeTypePush" to "Schupf", + "courtDrawingTool.strokeTypeSmash" to "Schuss", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Rückhand halblang", + "courtDrawingTool.targetBackhandLong" to "Rückhand lang", + "courtDrawingTool.targetBackhandShort" to "Rückhand kurz", + "courtDrawingTool.targetForehandHalfLong" to "Vorhand halblang", + "courtDrawingTool.targetForehandLong" to "Vorhand lang", + "courtDrawingTool.targetForehandShort" to "Vorhand kurz", + "courtDrawingTool.targetMiddleHalfLong" to "Mitte halblang", + "courtDrawingTool.targetMiddleLong" to "Mitte lang", + "courtDrawingTool.targetMiddleShort" to "Mitte kurz", + "courtDrawingTool.targetPosition" to "Zielposition:", + "courtDrawingTool.targetPositionLabel" to "Zielposition", + "courtDrawingTool.title" to "Tischtennis-Übungszeichnung", + "courtDrawingTool.titleLabel" to "Titel", + "courtDrawingTool.topspin" to "Überschnitt", + "courtDrawingTool.toTarget" to "nach", + "courtDrawingTool.underspin" to "Unterschnitt", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Aktiver Trainingstag", + "diary.activities" to "Aktivitäten", + "diary.activity" to "Aktivität", + "diary.activityDrawing" to "Aktivitätszeichnung", + "diary.activityImage" to "Aktivitätsbild", + "diary.activityNotFound" to "Aktivität nicht gefunden. Bitte wählen Sie eine aus der Liste aus.", + "diary.activityOrTimeblock" to "Aktivität / Zeitblock", + "diary.activityPlaceholder" to "Aktivität", + "diary.activityRequired" to "Bitte geben Sie eine Aktivität ein.", + "diary.addActivity" to "Aktivität hinzufügen", + "diary.addGroup" to "Gruppe hinzufügen", + "diary.addGroupActivity" to "Gruppen-Aktivität hinzufügen", + "diary.addGroupButton" to "+ Gruppe", + "diary.addTimeblock" to "Zeitblock", + "diary.all" to "Alle", + "diary.applySuggestion" to "Vorschlag übernehmen", + "diary.assignParticipants" to "Teilnehmer zuordnen", + "diary.assignParticipantsForGroupActivity" to "Teilnehmer für Gruppen-Aktivität zuordnen", + "diary.assignShort" to "Zuordnen", + "diary.bookAccident" to "Unfall buchen", + "diary.confirmDelete" to "Löschen bestätigen", + "diary.confirmDeleteDate" to "Möchten Sie dieses Datum wirklich löschen?", + "diary.confirmDeleteDateDetails" to "Alle zugehörigen Daten werden ebenfalls gelöscht.", + "diary.confirmDeleteGroup" to "Möchten Sie die Gruppe \"{name}\" wirklich löschen?", + "diary.createDate" to "Datum anlegen", + "diary.createDrawing" to "Übungszeichnung erstellen", + "diary.createGroups" to "Gruppen erstellen", + "diary.createNew" to "Neu anlegen", + "diary.createNewDate" to "Neues Datum anlegen", + "diary.date" to "Datum", + "diary.dateCannotBeDeleted" to "Datum kann nicht gelöscht werden", + "diary.dateCannotBeDeletedDetails" to "Es sind noch Inhalte vorhanden (Trainingplan, Teilnehmer, Aktivitäten, Unfälle oder Notizen).", + "diary.dateNoLongerCurrent" to "Ausgewähltes Datum war nicht mehr aktuell. Bitte erneut versuchen.", + "diary.delete" to "Löschen", + "diary.deleteDate" to "Datum löschen", + "diary.deleteGroup" to "Gruppe löschen", + "diary.duration" to "Dauer", + "diary.durationExampleLong" to "z.B. 2x7 oder 3*5", + "diary.durationExampleShort" to "z.B. 2x7", + "diary.durationMinutes" to "Dauer (Min)", + "diary.editActivity" to "Aktivität bearbeiten", + "diary.editGroupActivity" to "Gruppen-Aktivität bearbeiten", + "diary.editTrainingTimes" to "Trainingszeiten bearbeiten", + "diary.errorCreatingActivity" to "Fehler beim Erstellen der Aktivität", + "diary.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen", + "diary.errorDeletingGroup" to "Fehler beim Löschen der Gruppe", + "diary.errorLoadingPredefinedActivities" to "Fehler beim Laden der vordefinierten Aktivitäten", + "diary.errorMarkingForm" to "Fehler beim Markieren des Mitgliedsformulars", + "diary.errorOccurred" to "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "diary.existingGroups" to "Vorhandene Gruppen", + "diary.filterAbsent" to "Abwesend", + "diary.filterAll" to "Alle", + "diary.filterExcused" to "Entschuldigt", + "diary.filterPresent" to "Anwesend", + "diary.filterTest" to "Probe", + "diary.formHandedOver" to "Mitgliedsformular ausgehändigt", + "diary.formMarkedAsHandedOver" to "Mitgliedsformular als ausgehändigt markiert", + "diary.freeActivities" to "Freie Aktivitäten", + "diary.gallery" to "Mitglieder-Galerie", + "diary.galleryCreating" to "Galerie wird erstellt…", + "diary.group" to "Gruppe...", + "diary.groupDeletedSuccessfully" to "Gruppe wurde erfolgreich gelöscht!", + "diary.groupManagement" to "Gruppenverwaltung", + "diary.groupsCreated" to "{count} Gruppen wurden erfolgreich erstellt!", + "diary.groupsLabel" to "Gruppen", + "diary.groupsSection" to "Gruppen", + "diary.leader" to "Leiter", + "diary.min" to "Min", + "diary.minutes" to "Minuten", + "diary.mustCreateAtLeastTwoGroups" to "Beim ersten Erstellen müssen mindestens 2 Gruppen erstellt werden!", + "diary.nextAppointment" to "Nächster Termin", + "diary.noActiveTrainingDay" to "Kein Trainingstag ausgewählt.", + "diary.noEntries" to "Keine Einträge", + "diary.noFreeActivitiesYet" to "Noch keine freien Aktivitäten erfasst.", + "diary.noParticipants" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "diary.numberOfGroups" to "Anzahl Gruppen", + "diary.oneGroupAdded" to "1 Gruppe wurde erfolgreich hinzugefügt!", + "diary.openPlanItems" to "{count} offen", + "diary.openPlanItemsLabel" to "Plan-Status", + "diary.overallActivity" to "Gesamt-Aktivität", + "diary.participants" to "Teilnehmer", + "diary.participantStatusCancelled" to "Abgesagt", + "diary.participantStatusExcused" to "Entschuldigt", + "diary.participantStatusNone" to "Kein Status", + "diary.planActivitiesCount" to "Plan-Aktivitäten", + "diary.planAddHint" to "Neue Plan-Elemente fügst du über die Aktionen oben hinzu.", + "diary.planEmptyState" to "Im Trainingsplan ist noch nichts eingetragen.", + "diary.quickAdd" to "+ Schnell hinzufügen", + "diary.searchParticipants" to "Teilnehmer suchen", + "diary.selectGroup" to "Gruppe auswählen...", + "diary.selectGroupAndActivity" to "Bitte wählen Sie eine Gruppe und geben Sie eine Aktivität ein.", + "diary.selectParticipantAndNote" to "Bitte wählen Sie einen Teilnehmer aus und geben Sie einen Notiztext ein.", + "diary.selectTags" to "Tags auswählen", + "diary.selectTrainingGroup" to "Trainingsgruppe auswählen", + "diary.selectTrainingGroupPlaceholder" to "Bitte wählen...", + "diary.showImage" to "Bild/Zeichnung anzeigen", + "diary.skipSuggestion" to "Ohne Vorschlag fortfahren", + "diary.standardActivities" to "Standard-Aktivitäten", + "diary.standardActivityAddError" to "Standard-Aktivität konnte nicht hinzugefügt werden.", + "diary.standardDurationShort" to "Min", + "diary.startTime" to "Startzeit", + "diary.statusEmpty" to "Dieser Trainingstag ist noch leer.", + "diary.statusInProgress" to "Dieser Trainingstag ist teilweise vorbereitet.", + "diary.statusOpenShort" to "Offen", + "diary.statusReady" to "Zeiten und Trainingsplan sind gepflegt.", + "diary.statusReadyShort" to "Bereit", + "diary.suggestion" to "Vorschlag", + "diary.timeblock" to "Zeitblock", + "diary.timeblocksCount" to "Zeitblöcke", + "diary.title" to "Trainingstagebuch", + "diary.today" to "Heute", + "diary.trainingDayAsPDF" to "Trainingstag als PDF herunterladen", + "diary.trainingDayAsPDFShort" to "Trainingstag als PDF", + "diary.trainingDayChecklist" to "Abschlusscheck", + "diary.trainingDaySection" to "Trainingstag", + "diary.trainingDaySummaryPdfShort" to "Teilnehmerübersicht als PDF", + "diary.trainingEnd" to "Trainingsende", + "diary.trainingPlan" to "Trainingsplan", + "diary.trainingPlanAsPDF" to "Trainingsplan als PDF", + "diary.trainingPlanPdfShort" to "Ablaufplan als PDF", + "diary.trainingStart" to "Trainingsbeginn", + "diary.trainingTimesUpdated" to "Trainingszeiten erfolgreich aktualisiert.", + "diary.trainingWindow" to "Trainingsfenster", + "diary.trainingWindowUnset" to "Noch nicht gesetzt", + "diary.unassignedPlanItems" to "{count} offen", + "diary.updateTimes" to "Zeiten aktualisieren", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch", + "languages.de-CH" to "Schwiizerdütsch (Schweizer Deutsch)", + "languages.en-AU" to "English (Englisch (AU))", + "languages.en-GB" to "English (Englisch (GB))", + "languages.en-US" to "English (Englisch (US))", + "languages.es" to "Español (Spanisch)", + "languages.fil" to "Filipino (Filipino)", + "languages.fr" to "Français (Französisch)", + "languages.it" to "Italiano (Italienisch)", + "languages.ja" to "日本語 (Japanisch)", + "languages.pl" to "Polski (Polnisch)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog (Tagalog)", + "languages.zh" to "中文 (Chinesisch)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Aktionen", + "members.active" to "Aktiv", + "members.activeMembers" to "Aktive Mitglieder", + "members.addEmail" to "E-Mail-Adresse hinzufügen", + "members.addGroup" to "Gruppe hinzufügen...", + "members.addPhone" to "Telefonnummer hinzufügen", + "members.address" to "Adresse", + "members.adultReleaseApproved" to "Freigabe Erwachsene", + "members.adultReserveApproved" to "Ersatz bei Erwachsenen", + "members.adults" to "Erwachsene (18+)", + "members.age" to "Alter", + "members.ageFromPlaceholder" to "von", + "members.ageGroup" to "Altersklasse", + "members.ageRange" to "Alter von - bis", + "members.ageToPlaceholder" to "bis", + "members.batchFormsMarkedEmpty" to "In der aktuellen Auswahl gibt es keine ungeprüften Formulare.", + "members.batchFormsMarkedSuccess" to "{count} Formular(e) als geprüft markiert.", + "members.batchMarkedRegularEmpty" to "In der aktuellen Auswahl gibt es keine Probe-Mitglieder.", + "members.batchMarkedRegularSuccess" to "{count} Probe-Mitglied(er) als regulär markiert.", + "members.batchPartialFailure" to "{success} erfolgreich, {failed} fehlgeschlagen.", + "members.birthdate" to "Geburtsdatum", + "members.bulkActions" to "Sammelaktionen", + "members.camera" to "Kamera", + "members.change" to "Ändern", + "members.city" to "Ort", + "members.clearFields" to "Felder leeren", + "members.clearFilters" to "Filter zurücksetzen", + "members.clickTtRequestAction" to "Spielberechtigung in click-TT beantragen", + "members.clickTtRequestConfirm" to "Soll für {name} der automatisierte Click-TT-Antrag gestartet werden?", + "members.clickTtRequestError" to "Der Click-TT-Antrag konnte nicht eingereicht werden.", + "members.clickTtRequestFailedLog" to "Click-TT-Antrag fehlgeschlagen", + "members.clickTtRequestHint" to "Der Antrag wird im Backend automatisiert durch den Click-TT-Workflow geführt.", + "members.clickTtRequestPending" to "Click-TT-Antrag läuft", + "members.clickTtRequestPendingShort" to "Läuft ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Bitte loggen Sie sich noch auf click-tt ein und senden Sie den Antrag ab.", + "members.clickTtRequestTitle" to "Click-TT-Antrag starten", + "members.clickTtSubmitted" to "Click-TT gestellt", + "members.closeEditor" to "Editor schließen", + "members.composeEmail" to "E-Mail vorbereiten", + "members.contact" to "Kontakt", + "members.copyContactSummary" to "Kontaktübersicht kopieren", + "members.copyContactSummarySuccess" to "Kontaktübersicht in die Zwischenablage kopiert.", + "members.copyEmails" to "E-Mails kopieren", + "members.copyEmailsEmpty" to "In der aktuellen Auswahl gibt es keine E-Mail-Adressen.", + "members.copyEmailsSuccess" to "E-Mail-Liste in die Zwischenablage kopiert.", + "members.copyPhones" to "Telefone kopieren", + "members.copyPhonesEmpty" to "In der aktuellen Auswahl gibt es keine Telefonnummern.", + "members.copyPhonesSuccess" to "Telefonliste in die Zwischenablage kopiert.", + "members.create" to "Anlegen", + "members.createNewMember" to "Neues Mitglied anlegen", + "members.dataIssueAddress" to "Adresse fehlt", + "members.dataIssueBirthdate" to "Geburtsdatum fehlt", + "members.dataIssueCity" to "Ort fehlt", + "members.dataIssueEmail" to "E-Mail fehlt", + "members.dataIssueGender" to "Geschlecht ungeklärt", + "members.dataIssuePhone" to "Telefon fehlt", + "members.dataIssuePostalCode" to "PLZ fehlt", + "members.dataIssueStreet" to "Straße fehlt", + "members.dataIssueTrainingGroup" to "Trainingsgruppe fehlt", + "members.dataQuality" to "Datenqualität", + "members.dataQualityComplete" to "Daten vollständig", + "members.deactivateMember" to "Mitglied deaktivieren", + "members.deactivateMemberConfirm" to "Möchten Sie \"{name}\" wirklich deaktivieren?", + "members.deactivateMemberTitle" to "Mitglied deaktivieren", + "members.deleteImageConfirm" to "Möchten Sie dieses Bild wirklich löschen?", + "members.deleteImageTitle" to "Bild löschen", + "members.editHint" to "Ein Klick auf eine Zeile öffnet den Editor.", + "members.editMember" to "Mitglied bearbeiten", + "members.editorAssignTrainingGroupHint" to "Bitte mindestens eine Trainingsgruppe zuordnen.", + "members.editorCreateHint" to "Neues Mitglied anlegen und Kontaktdaten direkt erfassen.", + "members.editorEditHint" to "Daten von {name} bearbeiten.", + "members.editorRecommendedEntry" to "Empfohlener Eintrag", + "members.emailAddress" to "E-Mail-Adresse", + "members.emailAddressShort" to "Email-Adresse", + "members.emails" to "E-Mail-Adressen", + "members.errorAddingToGroup" to "Fehler beim Hinzufügen zur Gruppe", + "members.errorDeactivatingMember" to "Fehler beim Deaktivieren des Mitglieds", + "members.errorDeletingImage" to "Bild konnte nicht gelöscht werden", + "members.errorLoadingImage" to "Fehler beim Laden des Bildes", + "members.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "members.errorLoadingTrainingParticipations" to "Fehler beim Laden der Trainingsteilnahmen", + "members.errorMarkingForm" to "Fehler beim Markieren des Formulars.", + "members.errorRemovingFromGroup" to "Fehler beim Entfernen aus der Gruppe", + "members.errorRemovingTestMembership" to "Fehler beim Entfernen der Testmitgliedschaft.", + "members.errorRotatingImage" to "Fehler beim Drehen des Bildes", + "members.errorSavingMember" to "Fehler beim Speichern des Mitglieds", + "members.errorSettingPrimaryImage" to "Hauptbild konnte nicht gesetzt werden", + "members.errorTransfer" to "Fehler bei der Übertragung", + "members.errorUpdatingRatings" to "Fehler beim Aktualisieren der TTR/QTTR-Werte", + "members.errorUploadingImage" to "Bild konnte nicht hochgeladen werden", + "members.exercises" to "Übungen", + "members.exportCsv" to "CSV exportieren", + "members.exportCsvFile" to "mitglieder-auswahl.csv", + "members.exportEmails" to "E-Mail", + "members.exportMembersSelected" to "Mitglieder aktuell ausgewählt", + "members.exportPhones" to "Telefon", + "members.exportPreview" to "Exportvorschau", + "members.exportPreviewEmpty" to "Keine Mitglieder in der aktuellen Auswahl", + "members.exportPreviewNames" to "Vorschau", + "members.exportReachableByEmail" to "mit E-Mail-Adresse", + "members.exportReachableByPhone" to "mit Telefonnummer", + "members.firstName" to "Vorname", + "members.formHandedOver" to "Mitgliedsformular ausgehändigt", + "members.formMarkedAsHandedOver" to "Mitgliedsformular als ausgehändigt markiert.", + "members.gender" to "Geschlecht", + "members.genderDiverse" to "Divers", + "members.genderFemale" to "Weiblich", + "members.genderMale" to "Männlich", + "members.genderUnknown" to "Unbekannt", + "members.generatePhoneList" to "Telefonliste generieren", + "members.groupPhotoCrop" to "Gruppenfoto verarbeiten", + "members.groupPhotoCropSaved" to "Mitgliedsfoto aus Gruppenfoto gespeichert.", + "members.image" to "Bild", + "members.imageDeleted" to "Bild wurde gelöscht.", + "members.imageInternet" to "Bild (Inet?)", + "members.imagePreview" to "Vorschau des Mitgliedsbildes", + "members.imageUpdated" to "Bild wurde aktualisiert", + "members.inactive" to "inaktiv", + "members.inactiveMembers" to "Inaktive Mitglieder", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17 (17 und jünger)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Nachname", + "members.lastTrainingFilter" to "Letztes Training", + "members.lastTrainingFilterHasDate" to "Mit erfasstem Datum", + "members.lastTrainingFilterHint" to "AK-Spalte: laufende und nächste Saison. Weitere Details (letztes Training, Teilnahmen) auch beim Überfahren der Zeile.", + "members.lastTrainingFilterNoDate" to "Ohne letztes Training", + "members.lastTrainingFilterNotInTraining" to "„Nicht mehr im Training“", + "members.loadingMembers" to "Lade Mitglieder...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Formular geprüft", + "members.markFormsForSelection" to "Formulare für Auswahl prüfen", + "members.markRegular" to "Als regulär markieren", + "members.markRegularForSelection" to "Auswahl als regulär markieren", + "members.memberDeactivated" to "Mitglied deaktiviert", + "members.memberDetails" to "Mitgliedsdetails", + "members.memberFormHandedOver" to "Mitgliedsformular ausgehändigt", + "members.memberImage" to "Mitgliedsbild", + "members.memberImages" to "Mitgliedsbilder", + "members.memberInfo" to "Mitglieder-Info", + "members.memberScope" to "Mitgliedsbereich", + "members.missingTtrHistoryId" to "Keine myTischtennis-ID hinterlegt", + "members.name" to "Name, Vorname", + "members.newMember" to "Neues Mitglied", + "members.noAddressShort" to "Keine Adresse", + "members.noEmailShort" to "Keine E-Mail", + "members.noGroupsAssigned" to "Keine Gruppen zugeordnet", + "members.noGroupsAvailable" to "Keine Gruppen verfügbar", + "members.noOpenTasks" to "Keine offenen Aufgaben", + "members.noPhoneShort" to "Kein Telefon", + "members.notes" to "Notizen", + "members.noTestMembership" to "Keine Testmitgliedschaft mehr", + "members.notInTrainingTooltip" to "Seit {weeks} Trainingswochen ohne Teilnahme", + "members.onlyActiveMembers" to "Es werden nur aktive Mitglieder ausgegeben", + "members.openTasks" to "Offene Aufgaben", + "members.parent" to "Elternteil", + "members.parentFallback" to "Elternteil", + "members.parentName" to "Name (z.B. Mutter, Vater)", + "members.phoneList" to "Telefonliste.pdf", + "members.phoneListForSelection" to "Telefonliste für Auswahl", + "members.phoneListSelectionFile" to "Telefonliste-Auswahl.pdf", + "members.phoneNumber" to "Telefonnummer", + "members.phoneNumberShort" to "Telefon-Nr.", + "members.phones" to "Telefonnummern", + "members.picsInInternetAllowed" to "Pics in Internet erlaubt", + "members.postalCode" to "PLZ", + "members.previewLastTraining" to "Letztes Training", + "members.previewNoLastTraining" to "Noch keine Teilnahme", + "members.primary" to "Primär", + "members.primaryImageUpdated" to "Hauptbild wurde aktualisiert.", + "members.remove" to "Entfernen", + "members.resultsVisible" to "Mitglieder sichtbar", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Aktiv", + "members.scopeActiveDataIncomplete" to "Aktiv + Daten unvollständig", + "members.scopeActiveTest" to "Aktiv + Probe", + "members.scopeAll" to "Alle", + "members.scopeDataIncomplete" to "Daten unvollständig", + "members.scopeInactive" to "Inaktiv", + "members.scopeNeedsForm" to "Formular ungeprüft", + "members.scopeNotTraining" to "Nicht mehr im Training", + "members.scopeTest" to "Probe", + "members.search" to "Suche", + "members.searchAndFilter" to "Suche und Filter", + "members.searchPlaceholder" to "Name, Ort, Telefon oder E-Mail suchen", + "members.selectFile" to "Datei auswählen", + "members.showInactiveMembers" to "Inaktive Mitglieder anzeigen", + "members.showTrainingParticipationsColumn" to "Spalte „Trainingsteilnahmen“ anzeigen", + "members.showTtrHistory" to "TTR-Historie anzeigen", + "members.sixOrMoreParticipations" to "6 oder mehr Trainingsteilnahmen", + "members.sortAge" to "Alter", + "members.sortBirthday" to "Geburtstag", + "members.sortBy" to "Sortieren nach", + "members.sortFirstName" to "Vorname", + "members.sortLastName" to "Nachname", + "members.sortLastTraining" to "Letztes Training", + "members.sortOpenTasks" to "Offene Aufgaben", + "members.sortQttr" to "QTTR-Wert", + "members.status" to "Status", + "members.street" to "Straße", + "members.subtitle" to "Mitglieder suchen, filtern und direkt bearbeiten.", + "members.taskActionMarkRegular" to "Regulär setzen", + "members.taskActionRequest" to "Anfragen", + "members.taskActionReview" to "Öffnen", + "members.taskActionVerify" to "Prüfen", + "members.taskAssignTrainingGroup" to "Trainingsgruppe zuordnen", + "members.taskCheckClickTt" to "Click-TT-Spielberechtigung prüfen", + "members.taskCheckDataQuality" to "Datenqualität prüfen", + "members.taskCheckTrainingStatus" to "Trainingsstatus prüfen", + "members.taskReviewTrialStatus" to "Probe-Status prüfen", + "members.taskVerifyForm" to "Formular prüfen", + "members.testMember" to "Testm.", + "members.testMembers" to "Testmitglieder", + "members.testMembership" to "Testmitgliedschaft", + "members.testMembershipRemoved" to "Testmitgliedschaft entfernt.", + "members.threeOrMoreParticipations" to "3 oder mehr Trainingsteilnahmen", + "members.title" to "Mitglieder", + "members.toggleSortDirection" to "Sortierrichtung wechseln", + "members.trainingGroups" to "Trainingsgruppen", + "members.trainingParticipations" to "Trainingsteilnahmen", + "members.transferErrorTitle" to "Fehler bei der Übertragung", + "members.transferMembers" to "Mitglieder übertragen", + "members.transferSuccessTitle" to "Übertragung erfolgreich", + "members.ttAdult" to "Erwachsene (kein Jugend nach Stichtag)", + "members.ttAgeClassCol" to "AK (TT)", + "members.ttFilterGroupJ" to "Mädchen & Jungen (gemischt)", + "members.ttFilterGroupM" to "Mädchen (nur weiblich)", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "aktuell", + "members.ttSeasonFilter" to "Saison (Stichtag)", + "members.ttSeasonNextTag" to "kommend", + "members.ttStichtagHint" to "Stichtag 1.1. (DTTB). Jungen: nur J-Klassen. Mädchen: J und M möglich.", + "members.updateRatings" to "TTR/QTTR von myTischtennis aktualisieren", + "members.updating" to "Aktualisiere...", + "members.visibleMembers" to "Sichtbare Mitglieder", + "members.wantsToPlay" to "Will spielen", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Abbrechen", + "messages.confirm" to "Bestätigen", + "messages.error" to "Fehler", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Erfolg", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Warnung", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Freigaben", + "navigation.backToHome" to "Zur Startseite", + "navigation.billing" to "Abrechnung", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Vereinseinstellungen", + "navigation.clubTournaments" to "Vereinsturniere", + "navigation.competitions" to "Wettbewerbe", + "navigation.dailyBusiness" to "Tagesgeschäft", + "navigation.diary" to "Tagebuch", + "navigation.home" to "Startseite", + "navigation.login" to "Einloggen", + "navigation.logout" to "Ausloggen", + "navigation.logs" to "System-Logs", + "navigation.members" to "Mitglieder", + "navigation.memberTransfer" to "Mitgliederübertragung", + "navigation.myTischtennisAccount" to "myTischtennis-Account", + "navigation.orders" to "Bestellungen", + "navigation.permissions" to "Berechtigungen", + "navigation.personalSettings" to "Persönliche Einstellungen", + "navigation.predefinedActivities" to "Vordefinierte Aktivitäten", + "navigation.register" to "Registrieren", + "navigation.schedule" to "Spielpläne", + "navigation.settings" to "Einstellungen", + "navigation.statistics" to "Trainings-Statistik", + "navigation.teamManagement" to "Team-Verwaltung", + "navigation.tournamentParticipations" to "Turnierteilnahmen", + "navigation.tournaments" to "Turniere", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Bestellung anlegen", + "orders.budget" to "Budget", + "orders.club" to "Verein", + "orders.cost" to "Kosten", + "orders.dateAutoHint" to "Datum wird automatisch gesetzt und jede Änderung mit Datum protokolliert.", + "orders.errorLoading" to "Bestellungen konnten nicht geladen werden.", + "orders.errorSaving" to "Bestellung konnte nicht gespeichert werden.", + "orders.filterAllClubs" to "Alle Vereine", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Alle Status", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Hier werden alle Bestellungen vereinsübergreifend angezeigt und verwaltet.", + "orders.globalTitle" to "Bestellungen aller Vereine", + "orders.history" to "Verlauf", + "orders.item" to "Was", + "orders.itemPlaceholder" to "z. B. Trikot, Hoodie oder Schlägerhülle", + "orders.loading" to "Lade Bestellungen...", + "orders.member" to "Mitglied", + "orders.memberTitle" to "Bestellungen: {name}", + "orders.noOrdersGlobal" to "Es gibt aktuell keine Bestellungen.", + "orders.noOrdersMember" to "Für dieses Mitglied gibt es noch keine Bestellungen.", + "orders.open" to "Noch offen", + "orders.orderDate" to "Erfasst am", + "orders.paid" to "Bezahlt", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Nach Verein, Mitglied oder Artikel suchen", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "Artikel angekommen", + "orders.statusDate" to "Letzte Änderung", + "orders.statusHandedOver" to "Artikel ausgehändigt", + "orders.statusOrdered" to "bestellt", + "orders.statusRequested" to "gewünscht", + "orders.title" to "Bestellungen", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Verein:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Erstellt:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Spielklasse:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Meldung für:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Geplante Spielklasse:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Saison:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Team Altersklasse:", + "pdfGenerator.teamGenderLabel" to "Team Geschlecht:", + "pdfGenerator.teamLineupTitle" to "Mannschaftsaufstellung", + "pdfGenerator.teamNameLabel" to "Mannschaft:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Sprache", + "settings.languageChanged" to "Sprache erfolgreich geändert", + "settings.languageDescription" to "Wähle deine bevorzugte Sprache für die Anwendung", + "settings.personalSettings" to "Persönliche Einstellungen", + "settings.selectLanguage" to "Sprache auswählen", + "settings.title" to "Einstellungen", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Verfügbare Spieler", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Einsatz", + "teamManagement.eligibilityAdultRelease" to "Freigabe Erwachsene", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Freigabe + Ersatz Erwachsene", + "teamManagement.eligibilityAdultReserve" to "Ersatz Erwachsene", + "teamManagement.eligibilityRegular" to "Regulär", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Aufstellung als PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Vorrunde", + "teamManagement.firstHalfFull" to "Vorrunde (Juli - Dezember)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Bitte eine Spielklasse auswählen und speichern.", + "teamManagement.lineupEmpty" to "Noch keine Spieler für diese Mannschaft gemeldet.", + "teamManagement.lineupPdfEmpty" to "Es sind keine Spieler in der Aufstellung – PDF kann nicht erstellt werden.", + "teamManagement.lineupPdfFilePrefix" to "Aufstellung", + "teamManagement.lineupProposal" to "Mannschaftsmeldung nach QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Mannschaftsmeldung konnte nicht gespeichert werden.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} hat mehr als 30 QTTR Punkte Vorsprung vor {lower}. Diese Reihenfolge bitte korrigieren.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Fehlende Angaben:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "MyTischtennis-URL einfügen und parsen, um Team-ID und Ligendaten zu übernehmen.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Geplante Spielklasse", + "teamManagement.plannedLeagueHint" to "Optional. Unabhängig von der gemeldeten Liga (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "z. B. Bezirksliga, nach nächster Meldung …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Rückrunde", + "teamManagement.secondHalfFull" to "Rückrunde (ab 1. Januar)", + "teamManagement.selectedLineup" to "Gemeldete Spieler", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Pool-Regel hinzufügen", + "tournaments.address" to "Adresse", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Alle Teilnehmerdaten sind vollständig.", + "tournaments.allDataCompleteTop3" to "Alle Daten der Top-3-Platzierten sind vollständig.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} Teilnehmer brauchen eine Auswahl:", + "tournaments.atLeastOnePoolRule" to "Bitte mindestens eine Pool-Regel für {label} anlegen (z. B. Plätze 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} davon können automatisch zugeordnet werden.", + "tournaments.autoAssignEligible" to "Automatisch zuweisen", + "tournaments.autoAssignEligibleDone" to "{count} Teilnehmer wurden automatisch zugeordnet.", + "tournaments.autoAssignEligibleNone" to "Aktuell gibt es keine Teilnehmer mit eindeutiger automatischer Klassenzuweisung.", + "tournaments.autoAssignEligiblePartial" to "{successCount} Teilnehmer wurden automatisch zugeordnet, {errorCount} nicht.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Vorschläge:", + "tournaments.correctMatch" to "Korrigieren", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Endrunde aus Zwischenrunde erstellen", + "tournaments.createFinalFromPreliminary" to "Endrunde aus Vorrunde erstellen", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Zwischenrunde aus Vorrunde erstellen", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Partner bilden", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Noch nicht erfasst", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Möchten Sie die Klasse \"{name}\" wirklich löschen?", + "tournaments.deleteClassParticipantsDetached" to "Alle Teilnehmer werden von dieser Klasse entfernt.", + "tournaments.deleteClassTitle" to "Klasse löschen", + "tournaments.deleteExistingPairingsConfirm" to "Bestehende Paarungen werden gelöscht. Fortfahren?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Freie Tische verteilen", + "tournaments.distributeTablesResult" to "Tischverteilung", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} Teilnehmer in dieser Doppelklasse haben noch keinen Partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Passt zu: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Bitte geben Sie einen Klassennamen ein.", + "tournaments.enterExternalParticipantName" to "Bitte geben Sie mindestens Vorname und Nachname ein.", + "tournaments.enterMiniLocation" to "Bitte geben Sie einen Ort ein.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Fehler beim Verteilen der Tische.", + "tournaments.errorGeneratingPdf" to "Fehler beim Generieren des PDFs.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Endrunde", + "tournaments.finishMatch" to "Abschließen", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "PDF wird erstellt...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Gruppen", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Zwischenrunde (Runde 2)", + "tournaments.internalStatsAbsoluteRank" to "Rangliste Gesamtwertung", + "tournaments.internalStatsAgeFilter" to "Altersklassen & Geschlecht (Einzel)", + "tournaments.internalStatsAgeFilterAll" to "Alle Altersklassen", + "tournaments.internalStatsAgeFilterNone" to "Keine Altersklasse ausgewählt", + "tournaments.internalStatsAgeNoClass" to "Ohne Klassenzuordnung", + "tournaments.internalStatsAgeSelectAll" to "Alle", + "tournaments.internalStatsAgeSelectNone" to "Keine", + "tournaments.internalStatsAverageRank" to "Rangliste Durchschnitt (pro Turnier)", + "tournaments.internalStatsAvgPoints" to "Ø", + "tournaments.internalStatsEmpty" to "Keine auswertbaren Daten im gewählten Zeitraum.", + "tournaments.internalStatsExportPdf" to "Als PDF exportieren", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Letzte 12 Monate", + "tournaments.internalStatsLast3Months" to "Letzte 3 Monate", + "tournaments.internalStatsLast6Months" to "Letzte 6 Monate", + "tournaments.internalStatsOpenButton" to "Turnierstatistik (Einzel)", + "tournaments.internalStatsPeriod" to "Zeitraum", + "tournaments.internalStatsPoints" to "Summe", + "tournaments.internalStatsPointsExplain" to "Wertung: Pro Gruppe wird die Platzierung als Prozentzahl ausgedrückt (bei N Teilnehmern mit Platzierung: 1. = 100 %, Letzter = 0 %, dazwischen linear; gleiche Platzierung = gleicher Wert). N umfasst alle Platzierten in der Gruppe (inkl. Gäste). Bei nur einem Teilnehmer: 100 %. Wer die K.-o.-Runde erreicht, erhält den höchsten Gruppenwert der Klasse plus 1, danach je gewonnenes K.-o.-Spiel einen weiteren Punkt. Nur Vereinsmitglieder (Einzel). Die Filter J9–J19 / Erwachsene und Geschlecht („Alle“ = Weiblich- und Offen-Kanal, „Mädchen“ = nur Weiblich) beziehen sich auf das jeweilige Mitglied (Geburtsdatum und Geschlecht laut Vereinsdaten), nicht auf die Bezeichnung der Turnierklasse.", + "tournaments.internalStatsTitle" to "Statistik interne Turniere (Einzel)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} Turnier(e) im Zeitraum (ohne Minimeisterschaften).", + "tournaments.internalStatsTtAdult" to "Erwachsene", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Als laufend markieren", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Mindestens 2 Teilnehmer für Paarungen erforderlich.", + "tournaments.missingDataPDF" to "Fehlende Daten als PDF", + "tournaments.missingDataPDFSubtitle" to "Bitte die fehlenden Daten (markiert mit ____) bei den Teilnehmern erfragen und hier notieren.", + "tournaments.missingDataPDFSubtitleTop3" to "Fehlende Daten der ersten 3 Plätze (markiert mit ____) bitte erfragen und hier notieren.", + "tournaments.missingDataPDFTitle" to "Fehlende Teilnehmerdaten – Minimeisterschaft", + "tournaments.missingDataPDFTitleTop3" to "Fehlende Daten – Top 3 Minimeisterschaft", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Neuen Satz, z. B. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Keine Spiele verfügbar, bei denen beide Spieler frei sind.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "Keine passende Klasse gefunden.", + "tournaments.noFreeTables" to "Keine freien Tische verfügbar.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Keine verwaisten Spiele gefunden.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Noch keine Regeln. Beispiel: Plätze 1 und 2 -> obere Runde-2-Gruppen.", + "tournaments.noPoolRulesYetFinal" to "Noch keine Regeln. Beispiel: Plätze 1 und 2 -> Endrunde.", + "tournaments.noTop3Yet" to "Es stehen noch keine Top-3-Platzierungen fest.", + "tournaments.noTournamentDate" to "Kein Turnierdatum vorhanden.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Kein Trainingstag für {date} gefunden.", + "tournaments.noTrainingParticipants" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden.", + "tournaments.noValidTrainingParticipants" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Anzahl Tische", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} verwaiste Spiele entfernt.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Seite", + "tournaments.pairingAlreadyExists" to "Diese Paarung existiert bereits.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} Paarungen erstellt, {errorCount} Fehler.", + "tournaments.participantConflicts" to "Konflikte", + "tournaments.participantNotFound" to "Teilnehmer nicht gefunden.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Telefon", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Plätze aus jeder Gruppe (z. B. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Spieler 1", + "tournaments.playerTwo" to "Spieler 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Platz 3 ausspielen", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Regel {number}", + "tournaments.poolRulePlacesHelp" to "Beispiel: 1 oder 1,2", + "tournaments.poolRulePlacesLabel" to "Welche Plätze kommen weiter?", + "tournaments.poolRulePreview" to "Aus jeder Gruppe gehen Platz {places} weiter in {target}.", + "tournaments.poolRuleQuickExamples" to "Schnellbeispiele:", + "tournaments.poolRuleSummary" to "Plätze {places} gehen in {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} Zielgruppen", + "tournaments.poolRuleTargetKnockout" to "das K.-o.-Feld", + "tournaments.poolRuleTargetLabel" to "Wohin gehen diese Plätze?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Prüfe Datum, Name und Gewinnsätze.", + "tournaments.problemConfigTitle" to "Konfiguration unvollständig", + "tournaments.problemConflictsDescription" to "Prüfe unpassende Klassen, fehlende Daten oder unklare Zuordnungen.", + "tournaments.problemConflictsTitle" to "{count} Teilnehmer mit Konflikten", + "tournaments.problemDoublesAutoDescription" to "Die offene Doppelklasse kann direkt automatisch gepaart werden.", + "tournaments.problemDoublesDescription" to "Ein oder mehrere Doppelklassen brauchen noch Partnerzuordnungen.", + "tournaments.problemDoublesTitle" to "{count} offene Doppelpartner", + "tournaments.problemGroupMatchesDescription" to "Die Gruppen sind bereit, aber die Spiele wurden noch nicht erstellt.", + "tournaments.problemGroupMatchesTitle" to "Gruppenspiele noch nicht erzeugt", + "tournaments.problemGroupsMissingDescription" to "Für das Gruppenturnier müssen zuerst Gruppen angelegt werden.", + "tournaments.problemGroupsMissingTitle" to "Gruppen noch nicht erstellt", + "tournaments.problemKnockoutReadyDescription" to "Die Gruppenphase ist abgeschlossen und die Endrunde kann jetzt erzeugt werden.", + "tournaments.problemKnockoutReadyTitle" to "K.-o.-Runde kann gestartet werden", + "tournaments.problemUnassignedAutoDescription" to "{count} davon können direkt automatisch zugeordnet werden.", + "tournaments.problemUnassignedDescription" to "Diese Teilnehmer brauchen noch eine manuelle Klassenzuordnung.", + "tournaments.problemUnassignedTitle" to "{count} Teilnehmer ohne Klasse", + "tournaments.promotionRule12" to "Weiterkommen: Vorrunde → Zwischenrunde (1→2)", + "tournaments.promotionRuleDescription12" to "Hier legst du fest, welche Plätze aus jeder Vorrundengruppe in die Zwischenrunde kommen.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Hier legst du fest, welche Plätze aus der vorherigen Runde in die Gruppen der Endrunde kommen.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Hier legst du fest, welche Plätze aus der vorherigen Runde in das K.-o.-Feld kommen.", + "tournaments.promotionRuleFinal" to "Weiterkommen: Runde {from} → Runde {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Rangliste", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Anzahl Gruppen", + "tournaments.roundMode" to "Modus", + "tournaments.ruleStatusBlocked" to "Blockiert", + "tournaments.ruleStatusOk" to "Passt", + "tournaments.ruleStatusOkDescription" to "Diese Regel passt zur aktuellen Zielrunde und kann so verwendet werden.", + "tournaments.ruleStatusReview" to "Prüfen", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Runden speichern", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Bitte wählen Sie zuerst ein Turnier aus.", + "tournaments.selectTwoDifferentPlayers" to "Bitte wählen Sie zwei verschiedene Spieler aus.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Lege zuerst mindestens eine Weiterkommens-Regel an, bevor du Spieler in die nächste Runde übernimmst.", + "tournaments.stageActionRun" to "Jetzt ausführen", + "tournaments.stageActionsDescription" to "Diese Schritte übernehmen qualifizierte Spieler in die nächste Runde.", + "tournaments.stageActionsTitle" to "Nächste Aktionen", + "tournaments.stageConfigBadServerResponse" to "Fehlerhafte Antwort vom Server.", + "tournaments.stageConfigCurrentSetup" to "Aktuelle Struktur", + "tournaments.stageConfigIntro" to "Zwischenrunde ist optional. Wenn du sie aktivierst, gibt es danach immer eine Endrunde. KO-Endrunde wird als ein einziges Feld erzeugt.", + "tournaments.stageConfigLoadError" to "Fehler beim Laden der Rundenkonfiguration.", + "tournaments.stageConfigLoading" to "Lade Zwischenrunden …", + "tournaments.stageConfigMissingIds" to "Kann nicht speichern: Vereins- oder Turnier-ID fehlt.", + "tournaments.stageConfigTitle" to "Zwischenrunde & Endrunde", + "tournaments.stageCreated" to "Runde {round} wurde erstellt.", + "tournaments.stageFinalDescription" to "Bestimme, ob die Endrunde als Gruppenphase oder direkt als K.-o.-Feld gespielt wird.", + "tournaments.stageFlowBreakdownActionAddRule" to "Regel anlegen", + "tournaments.stageFlowBreakdownActionOpen" to "Regel öffnen", + "tournaments.stageFlowBreakdownActionReview" to "Fehler prüfen", + "tournaments.stageFlowBreakdownErrors" to "{count} Fehler blockieren", + "tournaments.stageFlowBreakdownMissingRule" to "Noch keine vollständige Regel angelegt", + "tournaments.stageFlowBreakdownOk" to "Konfiguration passt", + "tournaments.stageFlowBreakdownWarnings" to "{count} Hinweis(e) offen", + "tournaments.stageFlowDirectFinal" to "Vorrunde -> Endrunde ({final})", + "tournaments.stageFlowHealthBlocked" to "Rundenlogik widersprüchlich", + "tournaments.stageFlowHealthBlockedDescription" to "Mindestens eine Regel passt aktuell nicht zur Zielrunde oder enthält blockierende Konfigurationsfehler.", + "tournaments.stageFlowHealthIncomplete" to "Rundenlogik unvollständig", + "tournaments.stageFlowHealthMissingRules" to "Mindestens ein Übergang hat noch keine vollständige Weiterkommens-Regel.", + "tournaments.stageFlowHealthOk" to "Rundenlogik passt", + "tournaments.stageFlowHealthOkDescription" to "Die Übergänge zwischen den Runden sind vollständig konfiguriert und aktuell stimmig.", + "tournaments.stageFlowHealthReviewDescription" to "Die Rundenlogik ist grundsätzlich nutzbar, sollte aber wegen offener Hinweise noch geprüft werden.", + "tournaments.stageFlowPreviewMissing" to "Noch keine Regel konfiguriert.", + "tournaments.stageFlowPreviewRule" to "Plätze {places} gehen in {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (voraussichtlich {count} Qualifizierte)", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Platz {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Aktuell qualifiziert", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} Gruppe(n) sind noch nicht vollständig entschieden", + "tournaments.stageFlowReadinessNoGroups" to "Noch keine passenden Gruppen vorhanden", + "tournaments.stageFlowReadinessReady" to "Übergang ist fachlich startklar", + "tournaments.stageFlowRecommendationError" to "Prüfe zuerst den ersten blockierenden Fehler in der Rundenlogik.", + "tournaments.stageFlowRecommendationFixes" to "Die typischen Konfigurationsprobleme können direkt automatisch bereinigt werden.", + "tournaments.stageFlowRecommendationMissingRule" to "Für {label} fehlt noch eine vollständige Regel. Lege am besten damit los.", + "tournaments.stageFlowRecommendationSave" to "Die Rundenlogik ist stimmig. Du kannst die Konfiguration jetzt speichern.", + "tournaments.stageFlowRecommendationTitle" to "Empfohlener nächster Schritt", + "tournaments.stageFlowRecommendationWarnings" to "Es gibt noch Hinweise, die du vor dem Speichern prüfen solltest.", + "tournaments.stageFlowWithIntermediate" to "Vorrunde -> Zwischenrunde ({stage2}) -> Endrunde ({final})", + "tournaments.stageParticipantsTransferred" to "Qualifizierte Spieler wurden in {round} übernommen.", + "tournaments.stageStep1" to "Schritt 1", + "tournaments.stageStep1Description" to "Entscheide zuerst, ob nach der Vorrunde noch eine zusätzliche Runde gespielt werden soll.", + "tournaments.stageStep1Title" to "Zwischenrunde festlegen", + "tournaments.stageStep2" to "Schritt 2", + "tournaments.stageStep2Description" to "Lege fest, wie die Zwischenrunde aussieht und wie viele Gruppen dort gebraucht werden.", + "tournaments.stageStep3" to "Schritt 3", + "tournaments.stageValidationApplyAllFixes" to "{count} Schnellkorrektur(en) anwenden", + "tournaments.stageValidationApplyFix" to "Übernehmen", + "tournaments.stageValidationApplyTargetGroupFix" to "Auf {count} Zielgruppen setzen", + "tournaments.stageValidationApplyTargetTypeFix" to "Auf {target} umstellen", + "tournaments.stageValidationDescription" to "Diese Punkte solltest du vor dem Speichern oder Übernehmen der nächsten Runde korrigieren.", + "tournaments.stageValidationGroupCountRequired" to "Bitte mindestens eine gültige Gruppenanzahl festlegen.", + "tournaments.stageValidationKnockoutByesLikely" to "Mit voraussichtlich {count} Qualifizierten wird das K.-o.-Feld sehr wahrscheinlich Freilose enthalten.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "Für ein K.-o.-Feld werden mindestens 2 Qualifizierte benötigt.", + "tournaments.stageValidationKnockoutTargetOnly" to "Bei einer K.-o.-Endrunde ist hier nur das K.-o.-Feld als Ziel erlaubt.", + "tournaments.stageValidationNoRules" to "Für {stage} ist noch keine Weiterkommens-Regel angelegt.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "Mit nur {count} Qualifizierten für {groups} Zielgruppen würden leere Gruppen entstehen.", + "tournaments.stageValidationRulePlacesDuplicate" to "Eine Regel darf denselben Platz nicht mehrfach enthalten.", + "tournaments.stageValidationRulePlacesGap" to "Die Platzangabe hat Lücken. Meist ist eine zusammenhängende Reihenfolge wie 1,2 oder 1,2,3 gemeint.", + "tournaments.stageValidationRulePlacesInvalid" to "Die Platzangabe ist ungültig. Erlaubt sind nur positive ganze Zahlen wie 1 oder 1,2.", + "tournaments.stageValidationRulePlacesRequired" to "Trage mindestens einen Platz ein, der weiterkommen soll.", + "tournaments.stageValidationRulesOverlap" to "Platz {place} ist doppelt vergeben, nämlich in Regel {first} und Regel {second}.", + "tournaments.stageValidationSaveBlocked" to "Bitte zuerst die markierten Konfigurationsfehler korrigieren.", + "tournaments.stageValidationSuggestion" to "Vorschlag: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "Die Anzahl der Zielgruppen muss zur Zielrunde passen. Erwartet: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Bitte eine gültige Anzahl von Zielgruppen angeben.", + "tournaments.stageValidationTargetTypeMismatch" to "Das Ziel dieser Regel muss zur Zielrunde passen. Erwartet: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "Mit {count} Qualifizierten auf {groups} Zielgruppen werden die Gruppen vermutlich sehr klein.", + "tournaments.stageValidationTitle" to "Konfiguration prüfen", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Paaren", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Fertig", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Offen", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} Doppel ohne Partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Tisch", + "tournaments.tablesDistributed" to "Tische wurden verteilt.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Ziel", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Ziel-Gruppenanzahl", + "tournaments.targetGroupsLabel" to "{count} Gruppen", + "tournaments.tournamentClassGenderFemale" to "Weiblich", + "tournaments.tournamentClassGenderOpen" to "Alle", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Qualifizierte Spieler in die Endrunde übernehmen", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Nutzt die Regeln Zwischenrunde -> Endrunde und übernimmt die qualifizierten Teilnehmer.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Qualifizierte Spieler direkt in die Endrunde übernehmen", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Nutzt die Regeln Vorrunde -> Endrunde und erzeugt daraus direkt die qualifizierten Teilnehmer.", + "tournaments.transferQualifiedToIntermediate" to "Qualifizierte Spieler in die Zwischenrunde übernehmen", + "tournaments.transferQualifiedToIntermediateDesc" to "Nutzt die Regeln Vorrunde -> Zwischenrunde und übernimmt die qualifizierten Teilnehmer.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Laufend-Markierung entfernen", + "tournaments.unpairedDoubles" to "Doppel ohne Partner", + "tournaments.useIntermediateStage" to "Zwischenrunde verwenden", + "tournaments.warningBirthDateMissing" to "Geburtsdatum fehlt für diese Klasse", + "tournaments.warningClassMissing" to "Klasse nicht gefunden", + "tournaments.warningGenderMismatch" to "Geschlecht passt nicht zur Klasse", + "tournaments.warningGenderMissing" to "Geschlecht fehlt für diese Klasse", + "tournaments.warningMissingPairing" to "Doppelpartner fehlt", + "tournaments.warningTooOldForClass" to "Zu alt für diese Klasse", + "tournaments.warningTooYoungForClass" to "Zu jung für diese Klasse", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} offene Punkte", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Aktionen", + "trainingStats.activeMembers" to "Aktive Mitglieder", + "trainingStats.allTrainingDays" to "Alle Trainingstage", + "trainingStats.attendingMembers" to "Anwesende Mitglieder", + "trainingStats.averageParticipationCurrentMonth" to "Durchschnittliche Teilnahme (aktueller Monat)", + "trainingStats.averageParticipationHalfYear" to "Durchschnittliche Teilnahme (Halbjahr)", + "trainingStats.averageParticipationLastMonth" to "Durchschnittliche Teilnahme (letzter Monat)", + "trainingStats.averageParticipationQuarter" to "Durchschnittliche Teilnahme (Quartal)", + "trainingStats.averageParticipationYear" to "Durchschnittliche Teilnahme (Jahr)", + "trainingStats.birthdate" to "Geburtsdatum", + "trainingStats.date" to "Datum", + "trainingStats.lastTraining" to "Letztes Training", + "trainingStats.memberParticipations" to "Mitglieder-Teilnahmen", + "trainingStats.name" to "Name", + "trainingStats.noParticipants" to "Keine Teilnehmer", + "trainingStats.participants" to "Teilnehmer", + "trainingStats.participations12Months" to "Teilnahmen (12 Monate)", + "trainingStats.participations3Months" to "Teilnahmen (3 Monate)", + "trainingStats.participationsTotal" to "Teilnahmen (Gesamt)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Details anzeigen", + "trainingStats.title" to "Trainings-Statistik", + "trainingStats.trainingDayFilter" to "Trainingstag", + "trainingStats.trainingDays" to "Trainingstage (letzte 12 Monate)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Wochentag", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val en_AU: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Training Diary", + "app.title" to "Training Diary", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Activation failed. Please check the link or try again.", + "auth.confirmPassword" to "Confirm password", + "auth.email" to "Email", + "auth.forgotPassword" to "Forgot password?", + "auth.forgotPasswordDescription" to "Enter your email address. You will receive a link to reset your password.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Login", + "auth.loginFailed" to "Login failed. Please check your credentials and try again.", + "auth.loginSuccess" to "Successfully logged in", + "auth.logout" to "Logout", + "auth.logoutSuccess" to "Successfully logged out", + "auth.newPassword" to "New password", + "auth.noAccount" to "Don't have an account?", + "auth.password" to "Password", + "auth.passwordResetSuccess" to "Your password has been changed successfully. You can now log in with your new password.", + "auth.passwordsDoNotMatch" to "Passwords do not match.", + "auth.passwordTooShort" to "Password must be at least 6 characters long.", + "auth.register" to "Register", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Remember me", + "auth.resetEmailSent" to "If an account with this email exists, a reset link has been sent. Please check your inbox (and spam folder).", + "auth.resetFailed" to "Password could not be changed. The link may have expired.", + "auth.resetPassword" to "Set new password", + "auth.resetRequestFailed" to "Request failed. Please try again.", + "auth.saveNewPassword" to "Save password", + "auth.saving" to "Saving...", + "auth.sending" to "Sending...", + "auth.sendResetLink" to "Send link", + "auth.sessionExpired" to "Your session has expired. You will be logged out.", + "auth.toLogin" to "Go to login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Access to the club was denied.", + "club.accessRequested" to "Access has been requested.", + "club.accessRequestFailed" to "Access request could not be submitted.", + "club.accessRequestPending" to "Access to this club has been requested. Please be patient.", + "club.create" to "Create Club", + "club.createTitle" to "Create club", + "club.diary" to "Training diary", + "club.errorLoadingRequests" to "Failed to load open requests", + "club.load" to "Load", + "club.members" to "Members", + "club.mobileSelectHint" to "Please select a club first to use the app on your smartphone.", + "club.name" to "Club Name", + "club.new" to "New Club", + "club.noAccess" to "You have not yet been granted access to this club.", + "club.openAccessRequests" to "Open access requests", + "club.openRequests" to "Open access requests", + "club.requestAccess" to "Request access", + "club.select" to "Select Club", + "club.selectPlaceholder" to "Choose club...", + "club.title" to "Club", + "club.trainingDiary" to "Training diary", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Actions", + "common.active" to "Active", + "common.add" to "Add", + "common.all" to "All", + "common.apply" to "Apply", + "common.back" to "Back", + "common.cancel" to "Cancel", + "common.choose" to "Choose", + "common.clear" to "Clear", + "common.close" to "Close", + "common.confirm" to "Confirm", + "common.create" to "Create", + "common.date" to "Date", + "common.days" to "days", + "common.delete" to "Delete", + "common.description" to "Description", + "common.details" to "Details", + "common.disabled" to "Disabled", + "common.download" to "Download", + "common.edit" to "Edit", + "common.enabled" to "Enabled", + "common.filter" to "Filter", + "common.hours" to "hours", + "common.in" to "in", + "common.inactive" to "Inactive", + "common.loading" to "Loading...", + "common.min" to "min", + "common.minutes" to "minutes", + "common.months" to "months", + "common.move" to "Move", + "common.name" to "Name", + "common.new" to "New", + "common.next" to "Next", + "common.no" to "No", + "common.ok" to "OK", + "common.optional" to "Optional", + "common.period" to "Period", + "common.previous" to "Previous", + "common.refresh" to "Reload", + "common.remove" to "Remove", + "common.required" to "Required", + "common.reset" to "Reset", + "common.save" to "Save", + "common.saved" to "Saved", + "common.saving" to "Saving...", + "common.search" to "Search", + "common.select" to "Select", + "common.status" to "Status", + "common.submit" to "Submit", + "common.time" to "Time", + "common.today" to "Today", + "common.type" to "Type", + "common.update" to "Update", + "common.view" to "View", + "common.weeks" to "weeks", + "common.years" to "years", + "common.yes" to "Yes", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Add stroke", + "courtDrawingTool.backhand" to "Backhand", + "courtDrawingTool.codeLabel" to "Code", + "courtDrawingTool.completeFirstStroke" to "Complete the first stroke", + "courtDrawingTool.completeFirstStrokeHint" to "Choose start position, stroke side, spin and target. The graphic will then appear.", + "courtDrawingTool.configureExercise" to "Configure exercise", + "courtDrawingTool.counterSpin" to "Counter-spin", + "courtDrawingTool.forehand" to "Forehand", + "courtDrawingTool.noAdditionalStrokes" to "No follow-up strokes added yet.", + "courtDrawingTool.preview" to "Preview", + "courtDrawingTool.previewHint" to "The graphic appears as soon as the first stroke is fully configured.", + "courtDrawingTool.service" to "Serve:", + "courtDrawingTool.serviceTitle" to "Serve", + "courtDrawingTool.sidespin" to "Sidespin", + "courtDrawingTool.sideUnderspin" to "Side-backspin", + "courtDrawingTool.spin" to "Spin:", + "courtDrawingTool.startLeft" to "left", + "courtDrawingTool.startMiddle" to "centre", + "courtDrawingTool.startRight" to "right", + "courtDrawingTool.stepAdditionalStrokes" to "4. Follow-up strokes", + "courtDrawingTool.stepAdditionalStrokesHint" to "Optionally build additional balls as a sequence.", + "courtDrawingTool.stepFirstStroke" to "2. First stroke", + "courtDrawingTool.stepFirstStrokeHint" to "Set stroke side and spin for the first ball.", + "courtDrawingTool.stepStartPosition" to "1. Start position", + "courtDrawingTool.stepStartPositionHint" to "Which service position does the exercise start from?", + "courtDrawingTool.stepTarget" to "3. Target", + "courtDrawingTool.stepTargetHint" to "Choose the target zone for the first stroke.", + "courtDrawingTool.strokeSide" to "Side", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Chop defence", + "courtDrawingTool.strokeTypeCounter" to "Counter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Stroke type", + "courtDrawingTool.strokeTypeLobDefense" to "Lob defence", + "courtDrawingTool.strokeTypePush" to "Push", + "courtDrawingTool.strokeTypeSmash" to "Smash", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Backhand half-long", + "courtDrawingTool.targetBackhandLong" to "Backhand deep", + "courtDrawingTool.targetBackhandShort" to "Backhand short", + "courtDrawingTool.targetForehandHalfLong" to "Forehand half-long", + "courtDrawingTool.targetForehandLong" to "Forehand deep", + "courtDrawingTool.targetForehandShort" to "Forehand short", + "courtDrawingTool.targetMiddleHalfLong" to "Middle half-long", + "courtDrawingTool.targetMiddleLong" to "Middle deep", + "courtDrawingTool.targetMiddleShort" to "Middle short", + "courtDrawingTool.targetPosition" to "Target position:", + "courtDrawingTool.targetPositionLabel" to "Target position", + "courtDrawingTool.title" to "Table tennis exercise drawing", + "courtDrawingTool.titleLabel" to "Title", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "to", + "courtDrawingTool.underspin" to "Backspin", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Active training day", + "diary.activities" to "Activities", + "diary.activity" to "Activity", + "diary.activityDrawing" to "Activity drawing", + "diary.activityImage" to "Activity image", + "diary.activityNotFound" to "Activity not found. Please choose one from the list.", + "diary.activityOrTimeblock" to "Activity / time block", + "diary.activityPlaceholder" to "Activity", + "diary.activityRequired" to "Please enter an activity.", + "diary.addActivity" to "Add activity", + "diary.addGroup" to "Add group", + "diary.addGroupActivity" to "Add group activity", + "diary.addGroupButton" to "+ Group", + "diary.addTimeblock" to "Time block", + "diary.all" to "All", + "diary.applySuggestion" to "Apply suggestion", + "diary.assignParticipants" to "Assign participants", + "diary.assignParticipantsForGroupActivity" to "Assign participants for group activity", + "diary.assignShort" to "Assign", + "diary.bookAccident" to "Record accident", + "diary.confirmDelete" to "Confirm deletion", + "diary.confirmDeleteDate" to "Do you really want to delete this date?", + "diary.confirmDeleteDateDetails" to "All associated data will also be deleted.", + "diary.confirmDeleteGroup" to "Do you really want to delete the group \"{name}\"?", + "diary.createDate" to "Create date", + "diary.createDrawing" to "Create exercise drawing", + "diary.createGroups" to "Create groups", + "diary.createNew" to "Create new", + "diary.createNewDate" to "Create new date", + "diary.date" to "Date", + "diary.dateCannotBeDeleted" to "Date cannot be deleted", + "diary.dateCannotBeDeletedDetails" to "There is still content present (training plan, participants, activities, accidents or notes).", + "diary.dateNoLongerCurrent" to "The selected date was no longer current. Please try again.", + "diary.delete" to "Delete", + "diary.deleteDate" to "Delete date", + "diary.deleteGroup" to "Delete group", + "diary.duration" to "Duration", + "diary.durationExampleLong" to "e.g. 2x7 or 3*5", + "diary.durationExampleShort" to "e.g. 2x7", + "diary.durationMinutes" to "Duration (min)", + "diary.editActivity" to "Edit activity", + "diary.editGroupActivity" to "Edit group activity", + "diary.editTrainingTimes" to "Edit training times", + "diary.errorCreatingActivity" to "Error creating activity", + "diary.errorCreatingGroups" to "Error creating groups", + "diary.errorDeletingGroup" to "Error deleting group", + "diary.errorLoadingPredefinedActivities" to "Failed to load predefined activities", + "diary.errorMarkingForm" to "Error marking the membership form", + "diary.errorOccurred" to "An error occurred. Please try again.", + "diary.existingGroups" to "Existing groups", + "diary.filterAbsent" to "Absent", + "diary.filterAll" to "All", + "diary.filterExcused" to "Excused", + "diary.filterPresent" to "Present", + "diary.filterTest" to "Trial", + "diary.formHandedOver" to "Membership form handed over", + "diary.formMarkedAsHandedOver" to "Membership form marked as handed over", + "diary.freeActivities" to "Free activities", + "diary.gallery" to "Member gallery", + "diary.galleryCreating" to "Creating gallery…", + "diary.group" to "Group...", + "diary.groupDeletedSuccessfully" to "Group deleted successfully.", + "diary.groupManagement" to "Group management", + "diary.groupsCreated" to "{count} groups were created successfully.", + "diary.groupsLabel" to "Groups", + "diary.groupsSection" to "Groups", + "diary.leader" to "Leader", + "diary.min" to "Min", + "diary.minutes" to "Minutes", + "diary.mustCreateAtLeastTwoGroups" to "At least 2 groups must be created the first time.", + "diary.nextAppointment" to "Next appointment", + "diary.noActiveTrainingDay" to "No training day selected.", + "diary.noEntries" to "No entries", + "diary.noFreeActivitiesYet" to "No free activities have been recorded yet.", + "diary.noParticipants" to "No participants available for this training day.", + "diary.numberOfGroups" to "Number of groups", + "diary.oneGroupAdded" to "1 group was added successfully.", + "diary.openPlanItems" to "{count} open", + "diary.openPlanItemsLabel" to "Plan status", + "diary.overallActivity" to "Overall activity", + "diary.participants" to "Participants", + "diary.participantStatusCancelled" to "Cancelled", + "diary.participantStatusExcused" to "Excused", + "diary.participantStatusNone" to "No status", + "diary.planActivitiesCount" to "Planned activities", + "diary.planAddHint" to "Add new plan items using the actions above.", + "diary.planEmptyState" to "Nothing has been entered in the training plan yet.", + "diary.quickAdd" to "+ Quick add", + "diary.searchParticipants" to "Search participants", + "diary.selectGroup" to "Select group...", + "diary.selectGroupAndActivity" to "Please select a group and enter an activity.", + "diary.selectParticipantAndNote" to "Please select a participant and enter a note.", + "diary.selectTags" to "Select tags", + "diary.selectTrainingGroup" to "Select training group", + "diary.selectTrainingGroupPlaceholder" to "Please choose...", + "diary.showImage" to "Show image/drawing", + "diary.skipSuggestion" to "Continue without suggestion", + "diary.standardActivities" to "Standard activities", + "diary.standardActivityAddError" to "Standard activity could not be added.", + "diary.standardDurationShort" to "Min", + "diary.startTime" to "Start time", + "diary.statusEmpty" to "This training day is still empty.", + "diary.statusInProgress" to "This training day is partially prepared.", + "diary.statusOpenShort" to "Open", + "diary.statusReady" to "Times and training plan are set.", + "diary.statusReadyShort" to "Ready", + "diary.suggestion" to "Suggestion", + "diary.timeblock" to "Time block", + "diary.timeblocksCount" to "Time blocks", + "diary.title" to "Training diary", + "diary.today" to "Today", + "diary.trainingDayAsPDF" to "Download training day as PDF", + "diary.trainingDayAsPDFShort" to "Training day as PDF", + "diary.trainingDayChecklist" to "Completion checklist", + "diary.trainingDaySection" to "Training day", + "diary.trainingDaySummaryPdfShort" to "Participant summary as PDF", + "diary.trainingEnd" to "Training end", + "diary.trainingPlan" to "Training plan", + "diary.trainingPlanAsPDF" to "Training plan as PDF", + "diary.trainingPlanPdfShort" to "Schedule as PDF", + "diary.trainingStart" to "Training start", + "diary.trainingTimesUpdated" to "Training times updated successfully.", + "diary.trainingWindow" to "Training window", + "diary.trainingWindowUnset" to "Not set yet", + "diary.unassignedPlanItems" to "{count} open", + "diary.updateTimes" to "Update times", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (German)", + "languages.de-CH" to "Schwiizerdütsch (Swiss German)", + "languages.en-AU" to "English", + "languages.en-GB" to "English (British English)", + "languages.en-US" to "English (US English)", + "languages.es" to "Español (Spanish)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (French)", + "languages.it" to "Italiano (Italian)", + "languages.ja" to "日本語 (Japanese)", + "languages.pl" to "Polski (Polish)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Chinese)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Actions", + "members.active" to "Active", + "members.activeMembers" to "Active members", + "members.addEmail" to "Add email address", + "members.addGroup" to "Add group...", + "members.addPhone" to "Add phone number", + "members.address" to "Address", + "members.adultReleaseApproved" to "Approved for adults", + "members.adultReserveApproved" to "Adult reserve", + "members.adults" to "Adults (18+)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "from", + "members.ageGroup" to "Age group", + "members.ageRange" to "Age from - to", + "members.ageToPlaceholder" to "to", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "Birthdate", + "members.bulkActions" to "Bulk actions", + "members.camera" to "Camera", + "members.change" to "Change", + "members.city" to "City", + "members.clearFields" to "Clear fields", + "members.clearFilters" to "Reset filters", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "Create", + "members.createNewMember" to "Create new member", + "members.dataIssueAddress" to "Address missing", + "members.dataIssueBirthdate" to "Birthdate missing", + "members.dataIssueCity" to "Suburb/city missing", + "members.dataIssueEmail" to "Email missing", + "members.dataIssueGender" to "Gender unclear", + "members.dataIssuePhone" to "Phone missing", + "members.dataIssuePostalCode" to "Postcode missing", + "members.dataIssueStreet" to "Street missing", + "members.dataIssueTrainingGroup" to "Training group missing", + "members.dataQuality" to "Data quality", + "members.dataQualityComplete" to "Data complete", + "members.deactivateMember" to "Deactivate member", + "members.deactivateMemberConfirm" to "Do you really want to deactivate \"{name}\"?", + "members.deactivateMemberTitle" to "Deactivate member", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "Edit member", + "members.editorAssignTrainingGroupHint" to "Please assign at least one training group.", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "Recommended entry", + "members.emailAddress" to "Email address", + "members.emailAddressShort" to "Email address", + "members.emails" to "Email addresses", + "members.errorAddingToGroup" to "Error adding to group", + "members.errorDeactivatingMember" to "Error deactivating member", + "members.errorDeletingImage" to "Image could not be deleted", + "members.errorLoadingImage" to "Error loading image", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "Error loading training participations", + "members.errorMarkingForm" to "Error marking the form.", + "members.errorRemovingFromGroup" to "Error removing from group", + "members.errorRemovingTestMembership" to "Error removing the trial membership.", + "members.errorRotatingImage" to "Error rotating image", + "members.errorSavingMember" to "Error saving member", + "members.errorSettingPrimaryImage" to "Primary image could not be set", + "members.errorTransfer" to "Transfer error", + "members.errorUpdatingRatings" to "Error updating TTR/QTTR values", + "members.errorUploadingImage" to "Image could not be uploaded", + "members.exercises" to "Exercises", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "First name", + "members.formHandedOver" to "Membership form handed over", + "members.formMarkedAsHandedOver" to "Membership form marked as handed over.", + "members.gender" to "Gender", + "members.genderDiverse" to "Diverse", + "members.genderFemale" to "Female", + "members.genderMale" to "Male", + "members.genderUnknown" to "Unknown", + "members.generatePhoneList" to "Generate phone list", + "members.groupPhotoCrop" to "Process group photo", + "members.groupPhotoCropSaved" to "Member photo saved from group photo.", + "members.image" to "Image", + "members.imageDeleted" to "Image deleted.", + "members.imageInternet" to "Image (web?)", + "members.imagePreview" to "Member image preview", + "members.imageUpdated" to "Image updated", + "members.inactive" to "inactive", + "members.inactiveMembers" to "Inactive members", + "members.j11" to "U11", + "members.j13" to "U13", + "members.j15" to "U15", + "members.j17" to "U17 (17 and younger)", + "members.j19" to "U19", + "members.j9" to "U9", + "members.lastName" to "Last name", + "members.lastTrainingFilter" to "Last training", + "members.lastTrainingFilterHasDate" to "With a recorded date", + "members.lastTrainingFilterHint" to "Age-class column: current and next season. Extra detail (last training, participations) on row hover.", + "members.lastTrainingFilterNoDate" to "No last training", + "members.lastTrainingFilterNotInTraining" to "Flagged “no longer training”", + "members.loadingMembers" to "Loading members...", + "members.m11" to "G11", + "members.m13" to "G13", + "members.m15" to "G15", + "members.m19" to "G19", + "members.m9" to "G9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "Member deactivated", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "Membership form handed over", + "members.memberImage" to "Member image", + "members.memberImages" to "Member images", + "members.memberInfo" to "Member info", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "No myTischtennis ID stored", + "members.name" to "Surname, first name", + "members.newMember" to "New member", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "No groups assigned", + "members.noGroupsAvailable" to "No groups available", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "Notes", + "members.noTestMembership" to "No longer a trial member", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "Only active members are included", + "members.openTasks" to "Open tasks", + "members.parent" to "Parent", + "members.parentFallback" to "Parent", + "members.parentName" to "Name (e.g. mother, father)", + "members.phoneList" to "phone-list.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "Phone number", + "members.phoneNumberShort" to "Phone no.", + "members.phones" to "Phone numbers", + "members.picsInInternetAllowed" to "Pictures allowed online", + "members.postalCode" to "Postcode", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "Primary", + "members.primaryImageUpdated" to "Primary image updated.", + "members.remove" to "Remove", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "Active + data incomplete", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "Data incomplete", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "Search and filter", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "Select file", + "members.showInactiveMembers" to "Show inactive members", + "members.showTrainingParticipationsColumn" to "Show “Training participations” column", + "members.showTtrHistory" to "Show TTR history", + "members.sixOrMoreParticipations" to "6 or more training participations", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "QTTR value", + "members.status" to "Status", + "members.street" to "Street", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "Open", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "Assign training group", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "Review data quality", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "Trial", + "members.testMembers" to "Trial members", + "members.testMembership" to "Trial membership", + "members.testMembershipRemoved" to "Trial membership removed.", + "members.threeOrMoreParticipations" to "3 or more training participations", + "members.title" to "Members", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "Training groups", + "members.trainingParticipations" to "Training participations", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "Transfer members", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "Adults (not youth by cutoff)", + "members.ttAgeClassCol" to "Age class (TT)", + "members.ttFilterGroupJ" to "Boys & girls (mixed)", + "members.ttFilterGroupM" to "Girls only", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "current", + "members.ttSeasonFilter" to "Season (cutoff)", + "members.ttSeasonNextTag" to "upcoming", + "members.ttStichtagHint" to "Cutoff 1 Jan (DTTB). Boys: J classes only. Girls: J and M.", + "members.updateRatings" to "Update TTR/QTTR from myTischtennis", + "members.updating" to "Updating...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "Wants to play", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Cancel", + "messages.confirm" to "Confirm", + "messages.error" to "Error", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Success", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Warning", + "mobile.active" to "Active", + "mobile.add" to "Add", + "mobile.appLoading" to "Loading app", + "mobile.cancel" to "Cancel", + "mobile.clubRequest" to "Request access", + "mobile.clubRequested" to "Requested", + "mobile.createDiaryEntry" to "Create entry", + "mobile.deleteNote" to "Delete note", + "mobile.deleteTag" to "Remove tag", + "mobile.editTimes" to "Edit times", + "mobile.emailAndPasswordRequired" to "Email and password are required", + "mobile.entry" to "Entry", + "mobile.inactive" to "Inactive", + "mobile.language" to "Language", + "mobile.lastTraining" to "Last training", + "mobile.loginFailed" to "Login failed", + "mobile.loginInProgress" to "Signing in...", + "mobile.more" to "More", + "mobile.new" to "New", + "mobile.newNote" to "New note", + "mobile.newTag" to "New tag", + "mobile.noDiaryEntries" to "No diary entries yet", + "mobile.noMembers" to "No members found", + "mobile.noResults" to "No results", + "mobile.noTimes" to "No times", + "mobile.participationTop" to "Top attendance", + "mobile.refresh" to "Refresh", + "mobile.requestedAccess" to "Requested", + "mobile.role" to "Role", + "mobile.saveEntry" to "Save", + "mobile.search" to "Search", + "mobile.select" to "Select", + "mobile.sessionCheck" to "Check session", + "mobile.sessionCheckFailed" to "Session check failed", + "mobile.sessionInvalid" to "Session invalid", + "mobile.sessionValid" to "Session valid", + "mobile.status" to "Status", + "mobile.user" to "User", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Approvals", + "navigation.backToHome" to "Back to home", + "navigation.billing" to "Billing", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Club Settings", + "navigation.clubTournaments" to "Club Tournaments", + "navigation.competitions" to "Competitions", + "navigation.dailyBusiness" to "Daily Business", + "navigation.diary" to "Diary", + "navigation.home" to "Home", + "navigation.login" to "Login", + "navigation.logout" to "Logout", + "navigation.logs" to "System Logs", + "navigation.members" to "Members", + "navigation.memberTransfer" to "Member Transfer", + "navigation.myTischtennisAccount" to "myTischtennis Account", + "navigation.orders" to "Orders", + "navigation.permissions" to "Permissions", + "navigation.personalSettings" to "Personal Settings", + "navigation.predefinedActivities" to "Predefined Activities", + "navigation.register" to "Register", + "navigation.schedule" to "Schedules", + "navigation.settings" to "Settings", + "navigation.statistics" to "Training Statistics", + "navigation.teamManagement" to "Team Management", + "navigation.tournamentParticipations" to "Tournament Participations", + "navigation.tournaments" to "Tournaments", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Create order", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Cost", + "orders.dateAutoHint" to "The date is set automatically and every change is logged with a date.", + "orders.errorLoading" to "Orders could not be loaded.", + "orders.errorSaving" to "Order could not be saved.", + "orders.filterAllClubs" to "All clubs", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "All statuses", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "All orders across clubs can be viewed and managed here.", + "orders.globalTitle" to "Orders Across All Clubs", + "orders.history" to "History", + "orders.item" to "Item", + "orders.itemPlaceholder" to "e.g. shirt, hoodie or bat cover", + "orders.loading" to "Loading orders...", + "orders.member" to "Member", + "orders.memberTitle" to "Orders: {name}", + "orders.noOrdersGlobal" to "There are currently no orders.", + "orders.noOrdersMember" to "There are no orders for this member yet.", + "orders.open" to "Outstanding", + "orders.orderDate" to "Created on", + "orders.paid" to "Paid", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Search by club, member or item", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "item arrived", + "orders.statusDate" to "Last change", + "orders.statusHandedOver" to "item handed over", + "orders.statusOrdered" to "ordered", + "orders.statusRequested" to "requested", + "orders.title" to "Orders", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Generated:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "League:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Registration for:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Planned league:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Season:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Team age group:", + "pdfGenerator.teamGenderLabel" to "Team gender:", + "pdfGenerator.teamLineupTitle" to "Team line-up", + "pdfGenerator.teamNameLabel" to "Team:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Language", + "settings.languageChanged" to "Language successfully changed", + "settings.languageDescription" to "Choose your preferred language for the application", + "settings.personalSettings" to "Personal Settings", + "settings.selectLanguage" to "Select Language", + "settings.title" to "Settings", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Available players", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Eligibility", + "teamManagement.eligibilityAdultRelease" to "Approved for adults", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Approved + adult reserve", + "teamManagement.eligibilityAdultReserve" to "Adult reserve", + "teamManagement.eligibilityRegular" to "Regular", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Export line-up as PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "First half", + "teamManagement.firstHalfFull" to "First half (July - December)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Please select a league class and save.", + "teamManagement.lineupEmpty" to "No players have been assigned to this team yet.", + "teamManagement.lineupPdfEmpty" to "No players in the line-up – cannot create PDF.", + "teamManagement.lineupPdfFilePrefix" to "Line-up", + "teamManagement.lineupProposal" to "Line-up by QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Team line-up could not be saved.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} is more than 30 QTTR points ahead of {lower}. Please correct this order.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Missing:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Paste and parse the MyTischtennis URL to link the team ID and league data.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Planned league", + "teamManagement.plannedLeagueHint" to "Optional. Independent of the registered league (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "e.g. district league, after next registration …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Second half", + "teamManagement.secondHalfFull" to "Second half (from 1 January)", + "teamManagement.selectedLineup" to "Selected players", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Add pool rule", + "tournaments.address" to "Address", + "tournaments.adjustTournamentSearch" to "Adjust the search term or create a new tournament.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "All participant data is complete.", + "tournaments.allDataCompleteTop3" to "All data for the top 3 placed players is complete.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Please add at least one pool rule for {label} (e.g. places 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Correct", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Create final round from intermediate round", + "tournaments.createFinalFromPreliminary" to "Create final round from preliminary round", + "tournaments.createGroupMatches" to "Create group matches", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Create intermediate round from preliminary round", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Not yet recorded", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Do you really want to delete class \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "All participants will be detached from this class.", + "tournaments.deleteClassTitle" to "Delete class", + "tournaments.deleteExistingPairingsConfirm" to "Existing pairings will be deleted. Continue?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Distribute free tables", + "tournaments.distributeTablesResult" to "Table distribution", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doubles tournament", + "tournaments.doublesTournamentHint" to "Switches all existing classes to doubles and creates new classes as doubles by default.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Suitable for: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Please enter a class name.", + "tournaments.enterExternalParticipantName" to "Please enter at least first name and last name.", + "tournaments.enterMiniLocation" to "Please enter a location.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Error distributing tables.", + "tournaments.errorGeneratingPdf" to "Error generating the PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Final round", + "tournaments.finishMatch" to "Finish", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Generating PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Groups", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Intermediate round (round 2)", + "tournaments.internalStatsAbsoluteRank" to "Total score ranking", + "tournaments.internalStatsAgeFilter" to "Age group & gender (singles)", + "tournaments.internalStatsAgeFilterAll" to "All age classes", + "tournaments.internalStatsAgeFilterNone" to "No age class selected", + "tournaments.internalStatsAgeNoClass" to "No class assigned", + "tournaments.internalStatsAgeSelectAll" to "All", + "tournaments.internalStatsAgeSelectNone" to "None", + "tournaments.internalStatsAverageRank" to "Average per tournament", + "tournaments.internalStatsAvgPoints" to "Avg.", + "tournaments.internalStatsEmpty" to "No data for the selected period.", + "tournaments.internalStatsExportPdf" to "Export as PDF", + "tournaments.internalStatsFilterAgeBands" to "Age classes", + "tournaments.internalStatsFilterGenderColumn" to "Gender", + "tournaments.internalStatsGenderScopeAll" to "All", + "tournaments.internalStatsGenderScopeGirls" to "Girls only", + "tournaments.internalStatsLast12Months" to "Last 12 months", + "tournaments.internalStatsLast3Months" to "Last 3 months", + "tournaments.internalStatsLast6Months" to "Last 6 months", + "tournaments.internalStatsOpenButton" to "Tournament statistics (singles)", + "tournaments.internalStatsPeriod" to "Period", + "tournaments.internalStatsPoints" to "Total", + "tournaments.internalStatsPointsExplain" to "Scoring: In each group, placement is expressed as a percentage (with N ranked players: 1st = 100%, last = 0%, linear in between; tied ranks share the same value). N counts everyone ranked in that group (including guests). With only one player: 100%. Players who reach the knockout get the highest group score in that class plus 1, then one extra point per knockout match won. Club members in singles classes only.", + "tournaments.internalStatsTitle" to "Internal tournaments statistics (singles)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} tournament(s) in this period (excluding mini championships).", + "tournaments.internalStatsTtAdult" to "Adults", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Mark as live", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "At least 2 participants are required for pairings.", + "tournaments.missingDataPDF" to "Missing data as PDF", + "tournaments.missingDataPDFSubtitle" to "Please collect the missing data (marked with ____) from participants and note them here.", + "tournaments.missingDataPDFSubtitleTop3" to "Please collect missing data of the top 3 (marked with ____) and note them here.", + "tournaments.missingDataPDFTitle" to "Missing participant data – Mini championship", + "tournaments.missingDataPDFTitleTop3" to "Missing data – Top 3 Mini championship", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "New set, e.g. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "No matches available where both players are free.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "No suitable class found.", + "tournaments.noFreeTables" to "No free tables available.", + "tournaments.noMatchingTournamentsTitle" to "No matching tournaments", + "tournaments.noOrphanedMatchesFound" to "No orphaned matches found.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "No rules yet. Example: places 1 and 2 -> top round-2 groups.", + "tournaments.noPoolRulesYetFinal" to "No rules yet. Example: places 1 and 2 -> final round.", + "tournaments.noTop3Yet" to "No top 3 placements have been determined yet.", + "tournaments.noTournamentDate" to "No tournament date available.", + "tournaments.noTournamentsCreatedYet" to "No tournaments have been created yet.", + "tournaments.noTrainingOnDate" to "No training session found for {date}.", + "tournaments.noTrainingParticipants" to "No participants were found in the training session for this date.", + "tournaments.noValidTrainingParticipants" to "No valid participants were found in the training session for this date.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Number of tables", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} orphaned matches removed.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Page", + "tournaments.pairingAlreadyExists" to "This pairing already exists.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} pairings created, {errorCount} errors.", + "tournaments.participantConflicts" to "Conflicts", + "tournaments.participantNotFound" to "Participant not found.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} participants are not assigned to a class yet. These should be assigned first.", + "tournaments.phone" to "Phone", + "tournaments.placementsPendingFinals" to "Final placings will appear once multiple groups or a final round exist.", + "tournaments.placementsPendingGroups" to "Group placings will appear once group matches exist.", + "tournaments.placementsPendingNoGroups" to "Placings will appear here once group matches or a final round exist.", + "tournaments.placementsPendingSelectedClass" to "There are no placings for the currently selected class yet.", + "tournaments.placementsPendingTitle" to "Placings not available yet", + "tournaments.placesFromEachGroup" to "Places from each group (e.g. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Player 1", + "tournaments.playerTwo" to "Player 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Play third place match", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Advance: preliminary round → intermediate round (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Advance: round {from} → round {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Ranking", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Number of groups", + "tournaments.roundMode" to "Mode", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Save rounds", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Active tournament", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Please select a tournament first.", + "tournaments.selectTwoDifferentPlayers" to "Please select two different players.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} of {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Invalid server response.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "The intermediate round is optional. If you activate it, a final round follows afterwards. A knockout final round is created as a single bracket.", + "tournaments.stageConfigLoadError" to "Error loading round configuration.", + "tournaments.stageConfigLoading" to "Loading round configuration…", + "tournaments.stageConfigMissingIds" to "Cannot save: club or tournament ID is missing.", + "tournaments.stageConfigTitle" to "Intermediate & Final Round", + "tournaments.stageCreated" to "Round {round} was created.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Assign", + "tournaments.statusActionCreate" to "Create", + "tournaments.statusActionGenerate" to "Generate", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Review", + "tournaments.statusActionStart" to "Start", + "tournaments.statusConfigIncomplete" to "Configuration incomplete", + "tournaments.statusConfigReady" to "Configuration ready", + "tournaments.statusFinished" to "Finished", + "tournaments.statusGroupsMissing" to "Groups not created yet", + "tournaments.statusGroupsReady" to "Groups ready", + "tournaments.statusGroupsRunning" to "Group stage in progress", + "tournaments.statusKnockoutReady" to "Knockout ready to start", + "tournaments.statusKnockoutRunning" to "Knockout in progress", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Open", + "tournaments.statusParticipantsConflicts" to "{count} participants with conflicts", + "tournaments.statusParticipantsReady" to "{count} participants conflict-free", + "tournaments.statusParticipantsUnassigned" to "{count} without class", + "tournaments.statusTournamentCompleted" to "Tournament completed", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Table", + "tournaments.tablesDistributed" to "Tables have been distributed.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Target", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Target number of groups", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Female", + "tournaments.tournamentClassGenderOpen" to "Open (all)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Remove live marker", + "tournaments.unpairedDoubles" to "Doubles without partner", + "tournaments.useIntermediateStage" to "Use intermediate round", + "tournaments.warningBirthDateMissing" to "Birth date is missing for this class", + "tournaments.warningClassMissing" to "Class not found", + "tournaments.warningGenderMismatch" to "Gender does not match class", + "tournaments.warningGenderMissing" to "Gender is missing for this class", + "tournaments.warningMissingPairing" to "Doubles partner missing", + "tournaments.warningTooOldForClass" to "Too old for this class", + "tournaments.warningTooYoungForClass" to "Too young for this class", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Actions", + "trainingStats.activeMembers" to "Active members", + "trainingStats.allTrainingDays" to "All training days", + "trainingStats.attendingMembers" to "Attending members", + "trainingStats.averageParticipationCurrentMonth" to "Average participation (current month)", + "trainingStats.averageParticipationHalfYear" to "Average participation (half-year)", + "trainingStats.averageParticipationLastMonth" to "Average participation (last month)", + "trainingStats.averageParticipationQuarter" to "Average participation (quarter)", + "trainingStats.averageParticipationYear" to "Average participation (year)", + "trainingStats.birthdate" to "Birthdate", + "trainingStats.date" to "Date", + "trainingStats.lastTraining" to "Last training", + "trainingStats.memberParticipations" to "Member participations", + "trainingStats.name" to "Name", + "trainingStats.noParticipants" to "No participants", + "trainingStats.participants" to "Participants", + "trainingStats.participations12Months" to "Participations (12 months)", + "trainingStats.participations3Months" to "Participations (3 months)", + "trainingStats.participationsTotal" to "Participations (total)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Show details", + "trainingStats.title" to "Training statistics", + "trainingStats.trainingDayFilter" to "Training day", + "trainingStats.trainingDays" to "Training days (last 12 months)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Weekday", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val en_GB: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Training Diary", + "app.title" to "Training Diary", + "auth.accountActivated" to "Account activated! You can now log in.", + "auth.activate" to "Activate", + "auth.activateAccount" to "Activate account", + "auth.activationFailed" to "Activation failed. Please check the link or try again.", + "auth.confirmPassword" to "Confirm password", + "auth.email" to "Email", + "auth.forgotPassword" to "Forgot password?", + "auth.forgotPasswordDescription" to "Enter your email address. You will receive a link to reset your password.", + "auth.hasAccount" to "Already have an account?", + "auth.login" to "Login", + "auth.loginFailed" to "Login failed. Please check your credentials and try again.", + "auth.loginSuccess" to "Successfully logged in", + "auth.logout" to "Logout", + "auth.logoutSuccess" to "Successfully logged out", + "auth.newPassword" to "New password", + "auth.noAccount" to "Don't have an account?", + "auth.password" to "Password", + "auth.passwordResetSuccess" to "Your password has been changed successfully. You can now log in with your new password.", + "auth.passwordsDoNotMatch" to "Passwords do not match.", + "auth.passwordTooShort" to "Password must be at least 6 characters long.", + "auth.register" to "Register", + "auth.registerFailed" to "Registration failed. Please try again.", + "auth.registerSuccess" to "Registration successful! Please check your email to activate your account.", + "auth.rememberMe" to "Remember me", + "auth.resetEmailSent" to "If an account with this email exists, a reset link has been sent. Please check your inbox (and spam folder).", + "auth.resetFailed" to "Password could not be changed. The link may have expired.", + "auth.resetPassword" to "Set new password", + "auth.resetRequestFailed" to "Request failed. Please try again.", + "auth.saveNewPassword" to "Save password", + "auth.saving" to "Saving...", + "auth.sending" to "Sending...", + "auth.sendResetLink" to "Send link", + "auth.sessionExpired" to "Your session has expired. You will be logged out.", + "auth.toLogin" to "Go to login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Access to the club was denied.", + "club.accessRequested" to "Access has been requested.", + "club.accessRequestFailed" to "Access request could not be submitted.", + "club.accessRequestPending" to "Access to this club has been requested. Please be patient.", + "club.create" to "Create Club", + "club.createTitle" to "Create club", + "club.diary" to "Training diary", + "club.errorLoadingRequests" to "Failed to load open requests", + "club.load" to "Load", + "club.members" to "Members", + "club.mobileSelectHint" to "Please select a club first to use the app on your smartphone.", + "club.name" to "Club Name", + "club.new" to "New Club", + "club.noAccess" to "You have not yet been granted access to this club.", + "club.openAccessRequests" to "Open access requests", + "club.openRequests" to "Open access requests", + "club.requestAccess" to "Request access", + "club.select" to "Select Club", + "club.selectPlaceholder" to "Choose club...", + "club.title" to "Club", + "club.trainingDiary" to "Training diary", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Actions", + "common.active" to "Active", + "common.add" to "Add", + "common.all" to "All", + "common.apply" to "Apply", + "common.back" to "Back", + "common.cancel" to "Cancel", + "common.choose" to "Choose", + "common.clear" to "Clear", + "common.close" to "Close", + "common.confirm" to "Confirm", + "common.create" to "Create", + "common.date" to "Date", + "common.days" to "days", + "common.delete" to "Delete", + "common.description" to "Description", + "common.details" to "Details", + "common.disabled" to "Disabled", + "common.download" to "Download", + "common.edit" to "Edit", + "common.enabled" to "Enabled", + "common.filter" to "Filter", + "common.hours" to "hours", + "common.in" to "in", + "common.inactive" to "Inactive", + "common.loading" to "Loading...", + "common.min" to "min", + "common.minutes" to "minutes", + "common.months" to "months", + "common.move" to "Move", + "common.name" to "Name", + "common.new" to "New", + "common.next" to "Next", + "common.no" to "No", + "common.ok" to "OK", + "common.optional" to "Optional", + "common.period" to "Period", + "common.previous" to "Previous", + "common.refresh" to "Reload", + "common.remove" to "Remove", + "common.required" to "Required", + "common.reset" to "Reset", + "common.save" to "Save", + "common.saved" to "Saved", + "common.saving" to "Saving...", + "common.search" to "Search", + "common.select" to "Select", + "common.status" to "Status", + "common.submit" to "Submit", + "common.time" to "Time", + "common.today" to "Today", + "common.type" to "Type", + "common.update" to "Update", + "common.view" to "View", + "common.weeks" to "weeks", + "common.years" to "years", + "common.yes" to "Yes", + "courtDrawing.cancel" to "Cancel", + "courtDrawing.durationMinutes" to "Duration (minutes)", + "courtDrawing.durationText" to "Duration (text)", + "courtDrawing.durationTextPlaceholder" to "e.g. 2x5", + "courtDrawing.group" to "Group", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Select group...", + "courtDrawing.title" to "Configure table tennis exercise", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Add stroke", + "courtDrawingTool.backhand" to "Backhand", + "courtDrawingTool.codeLabel" to "Code", + "courtDrawingTool.completeFirstStroke" to "Complete the first stroke", + "courtDrawingTool.completeFirstStrokeHint" to "Choose start position, stroke side, spin and target. The graphic will then appear.", + "courtDrawingTool.configureExercise" to "Configure exercise", + "courtDrawingTool.counterSpin" to "Counter-spin", + "courtDrawingTool.forehand" to "Forehand", + "courtDrawingTool.noAdditionalStrokes" to "No follow-up strokes added yet.", + "courtDrawingTool.preview" to "Preview", + "courtDrawingTool.previewHint" to "The graphic appears as soon as the first stroke is fully configured.", + "courtDrawingTool.service" to "Serve:", + "courtDrawingTool.serviceTitle" to "Serve", + "courtDrawingTool.sidespin" to "Sidespin", + "courtDrawingTool.sideUnderspin" to "Side-backspin", + "courtDrawingTool.spin" to "Spin:", + "courtDrawingTool.startLeft" to "left", + "courtDrawingTool.startMiddle" to "centre", + "courtDrawingTool.startRight" to "right", + "courtDrawingTool.stepAdditionalStrokes" to "4. Follow-up strokes", + "courtDrawingTool.stepAdditionalStrokesHint" to "Optionally build additional balls as a sequence.", + "courtDrawingTool.stepFirstStroke" to "2. First stroke", + "courtDrawingTool.stepFirstStrokeHint" to "Set stroke side and spin for the first ball.", + "courtDrawingTool.stepStartPosition" to "1. Start position", + "courtDrawingTool.stepStartPositionHint" to "Which service position does the exercise start from?", + "courtDrawingTool.stepTarget" to "3. Target", + "courtDrawingTool.stepTargetHint" to "Choose the target zone for the first stroke.", + "courtDrawingTool.strokeSide" to "Side", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Chop defence", + "courtDrawingTool.strokeTypeCounter" to "Counter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Stroke type", + "courtDrawingTool.strokeTypeLobDefense" to "Lob defence", + "courtDrawingTool.strokeTypePush" to "Push", + "courtDrawingTool.strokeTypeSmash" to "Smash", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Backhand half-long", + "courtDrawingTool.targetBackhandLong" to "Backhand deep", + "courtDrawingTool.targetBackhandShort" to "Backhand short", + "courtDrawingTool.targetForehandHalfLong" to "Forehand half-long", + "courtDrawingTool.targetForehandLong" to "Forehand deep", + "courtDrawingTool.targetForehandShort" to "Forehand short", + "courtDrawingTool.targetMiddleHalfLong" to "Middle half-long", + "courtDrawingTool.targetMiddleLong" to "Middle deep", + "courtDrawingTool.targetMiddleShort" to "Middle short", + "courtDrawingTool.targetPosition" to "Target position:", + "courtDrawingTool.targetPositionLabel" to "Target position", + "courtDrawingTool.title" to "Table tennis exercise drawing", + "courtDrawingTool.titleLabel" to "Title", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "to", + "courtDrawingTool.underspin" to "Backspin", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Active training day", + "diary.activities" to "Activities", + "diary.activity" to "Activity", + "diary.activityDrawing" to "Activity drawing", + "diary.activityImage" to "Activity image", + "diary.activityNotFound" to "Activity not found. Please choose one from the list.", + "diary.activityOrTimeblock" to "Activity / time block", + "diary.activityPlaceholder" to "Activity", + "diary.activityRequired" to "Please enter an activity.", + "diary.addActivity" to "Add activity", + "diary.addGroup" to "Add group", + "diary.addGroupActivity" to "Add group activity", + "diary.addGroupButton" to "+ Group", + "diary.addTimeblock" to "Time block", + "diary.all" to "All", + "diary.applySuggestion" to "Apply suggestion", + "diary.assignParticipants" to "Assign participants", + "diary.assignParticipantsForGroupActivity" to "Assign participants for group activity", + "diary.assignShort" to "Assign", + "diary.bookAccident" to "Record accident", + "diary.confirmDelete" to "Confirm deletion", + "diary.confirmDeleteDate" to "Do you really want to delete this date?", + "diary.confirmDeleteDateDetails" to "All associated data will also be deleted.", + "diary.confirmDeleteGroup" to "Do you really want to delete the group \"{name}\"?", + "diary.createDate" to "Create date", + "diary.createDrawing" to "Create exercise drawing", + "diary.createGroups" to "Create groups", + "diary.createNew" to "Create new", + "diary.createNewDate" to "Create new date", + "diary.date" to "Date", + "diary.dateCannotBeDeleted" to "Date cannot be deleted", + "diary.dateCannotBeDeletedDetails" to "There is still content present (training plan, participants, activities, accidents or notes).", + "diary.dateNoLongerCurrent" to "The selected date was no longer current. Please try again.", + "diary.delete" to "Delete", + "diary.deleteDate" to "Delete date", + "diary.deleteGroup" to "Delete group", + "diary.duration" to "Duration", + "diary.durationExampleLong" to "e.g. 2x7 or 3*5", + "diary.durationExampleShort" to "e.g. 2x7", + "diary.durationMinutes" to "Duration (min)", + "diary.editActivity" to "Edit activity", + "diary.editGroupActivity" to "Edit group activity", + "diary.editTrainingTimes" to "Edit training times", + "diary.errorCreatingActivity" to "Error creating activity", + "diary.errorCreatingGroups" to "Error creating groups", + "diary.errorDeletingGroup" to "Error deleting group", + "diary.errorLoadingPredefinedActivities" to "Failed to load predefined activities", + "diary.errorMarkingForm" to "Error marking the membership form", + "diary.errorOccurred" to "An error occurred. Please try again.", + "diary.existingGroups" to "Existing groups", + "diary.filterAbsent" to "Absent", + "diary.filterAll" to "All", + "diary.filterExcused" to "Excused", + "diary.filterPresent" to "Present", + "diary.filterTest" to "Trial", + "diary.formHandedOver" to "Membership form handed over", + "diary.formMarkedAsHandedOver" to "Membership form marked as handed over", + "diary.freeActivities" to "Free activities", + "diary.gallery" to "Member gallery", + "diary.galleryCreating" to "Creating gallery…", + "diary.group" to "Group...", + "diary.groupDeletedSuccessfully" to "Group deleted successfully.", + "diary.groupManagement" to "Group management", + "diary.groupsCreated" to "{count} groups were created successfully.", + "diary.groupsLabel" to "Groups", + "diary.groupsSection" to "Groups", + "diary.leader" to "Leader", + "diary.min" to "Min", + "diary.minutes" to "Minutes", + "diary.mustCreateAtLeastTwoGroups" to "At least 2 groups must be created the first time.", + "diary.nextAppointment" to "Next appointment", + "diary.noActiveTrainingDay" to "No training day selected.", + "diary.noEntries" to "No entries", + "diary.noFreeActivitiesYet" to "No free activities have been recorded yet.", + "diary.noParticipants" to "No participants available for this training day.", + "diary.numberOfGroups" to "Number of groups", + "diary.oneGroupAdded" to "1 group was added successfully.", + "diary.openPlanItems" to "{count} open", + "diary.openPlanItemsLabel" to "Plan status", + "diary.overallActivity" to "Overall activity", + "diary.participants" to "Participants", + "diary.participantStatusCancelled" to "Cancelled", + "diary.participantStatusExcused" to "Excused", + "diary.participantStatusNone" to "No status", + "diary.planActivitiesCount" to "Planned activities", + "diary.planAddHint" to "Add new plan items using the actions above.", + "diary.planEmptyState" to "Nothing has been entered in the training plan yet.", + "diary.quickAdd" to "+ Quick add", + "diary.searchParticipants" to "Search participants", + "diary.selectGroup" to "Select group...", + "diary.selectGroupAndActivity" to "Please select a group and enter an activity.", + "diary.selectParticipantAndNote" to "Please select a participant and enter a note.", + "diary.selectTags" to "Select tags", + "diary.selectTrainingGroup" to "Select training group", + "diary.selectTrainingGroupPlaceholder" to "Please choose...", + "diary.showImage" to "Show image/drawing", + "diary.skipSuggestion" to "Continue without suggestion", + "diary.standardActivities" to "Standard activities", + "diary.standardActivityAddError" to "Standard activity could not be added.", + "diary.standardDurationShort" to "Min", + "diary.startTime" to "Start time", + "diary.statusEmpty" to "This training day is still empty.", + "diary.statusInProgress" to "This training day is partially prepared.", + "diary.statusOpenShort" to "Open", + "diary.statusReady" to "Times and training plan are set.", + "diary.statusReadyShort" to "Ready", + "diary.suggestion" to "Suggestion", + "diary.timeblock" to "Time block", + "diary.timeblocksCount" to "Time blocks", + "diary.title" to "Training diary", + "diary.today" to "Today", + "diary.trainingDayAsPDF" to "Download training day as PDF", + "diary.trainingDayAsPDFShort" to "Training day as PDF", + "diary.trainingDayChecklist" to "Completion checklist", + "diary.trainingDaySection" to "Training day", + "diary.trainingDaySummaryPdfShort" to "Participant summary as PDF", + "diary.trainingEnd" to "Training end", + "diary.trainingPlan" to "Training plan", + "diary.trainingPlanAsPDF" to "Training plan as PDF", + "diary.trainingPlanPdfShort" to "Schedule as PDF", + "diary.trainingStart" to "Training start", + "diary.trainingTimesUpdated" to "Training times updated successfully.", + "diary.trainingWindow" to "Training window", + "diary.trainingWindowUnset" to "Not set yet", + "diary.unassignedPlanItems" to "{count} open", + "diary.updateTimes" to "Update times", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (German)", + "languages.de-CH" to "Schwiizerdütsch (Swiss German)", + "languages.en-AU" to "English (Australian English)", + "languages.en-GB" to "English", + "languages.en-US" to "English (US English)", + "languages.es" to "Español (Spanish)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (French)", + "languages.it" to "Italiano (Italian)", + "languages.ja" to "日本語 (Japanese)", + "languages.pl" to "Polski (Polish)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Chinese)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Actions", + "members.active" to "Active", + "members.activeMembers" to "Active members", + "members.addEmail" to "Add email address", + "members.addGroup" to "Add group...", + "members.addPhone" to "Add phone number", + "members.address" to "Address", + "members.adultReleaseApproved" to "Approved for adults", + "members.adultReserveApproved" to "Adult reserve", + "members.adults" to "Adults (18+)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "from", + "members.ageGroup" to "Age group", + "members.ageRange" to "Age from - to", + "members.ageToPlaceholder" to "to", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "Birthdate", + "members.bulkActions" to "Bulk actions", + "members.camera" to "Camera", + "members.change" to "Change", + "members.city" to "City", + "members.clearFields" to "Clear fields", + "members.clearFilters" to "Reset filters", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "Create", + "members.createNewMember" to "Create new member", + "members.dataIssueAddress" to "Address missing", + "members.dataIssueBirthdate" to "Birthdate missing", + "members.dataIssueCity" to "Town/city missing", + "members.dataIssueEmail" to "Email missing", + "members.dataIssueGender" to "Gender unclear", + "members.dataIssuePhone" to "Phone missing", + "members.dataIssuePostalCode" to "Postcode missing", + "members.dataIssueStreet" to "Street missing", + "members.dataIssueTrainingGroup" to "Training group missing", + "members.dataQuality" to "Data quality", + "members.dataQualityComplete" to "Data complete", + "members.deactivateMember" to "Deactivate member", + "members.deactivateMemberConfirm" to "Do you really want to deactivate \"{name}\"?", + "members.deactivateMemberTitle" to "Deactivate member", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "Edit member", + "members.editorAssignTrainingGroupHint" to "Please assign at least one training group.", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "Recommended entry", + "members.emailAddress" to "Email address", + "members.emailAddressShort" to "Email address", + "members.emails" to "Email addresses", + "members.errorAddingToGroup" to "Error adding to group", + "members.errorDeactivatingMember" to "Error deactivating member", + "members.errorDeletingImage" to "Image could not be deleted", + "members.errorLoadingImage" to "Error loading image", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "Error loading training participations", + "members.errorMarkingForm" to "Error marking the form.", + "members.errorRemovingFromGroup" to "Error removing from group", + "members.errorRemovingTestMembership" to "Error removing the trial membership.", + "members.errorRotatingImage" to "Error rotating image", + "members.errorSavingMember" to "Error saving member", + "members.errorSettingPrimaryImage" to "Primary image could not be set", + "members.errorTransfer" to "Transfer error", + "members.errorUpdatingRatings" to "Error updating TTR/QTTR values", + "members.errorUploadingImage" to "Image could not be uploaded", + "members.exercises" to "Exercises", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "First name", + "members.formHandedOver" to "Membership form handed over", + "members.formMarkedAsHandedOver" to "Membership form marked as handed over.", + "members.gender" to "Gender", + "members.genderDiverse" to "Diverse", + "members.genderFemale" to "Female", + "members.genderMale" to "Male", + "members.genderUnknown" to "Unknown", + "members.generatePhoneList" to "Generate phone list", + "members.groupPhotoCrop" to "Process group photo", + "members.groupPhotoCropSaved" to "Member photo saved from group photo.", + "members.image" to "Image", + "members.imageDeleted" to "Image deleted.", + "members.imageInternet" to "Image (web?)", + "members.imagePreview" to "Member image preview", + "members.imageUpdated" to "Image updated", + "members.inactive" to "inactive", + "members.inactiveMembers" to "Inactive members", + "members.j11" to "U11", + "members.j13" to "U13", + "members.j15" to "U15", + "members.j17" to "U17 (17 and younger)", + "members.j19" to "U19", + "members.j9" to "U9", + "members.lastName" to "Last name", + "members.lastTrainingFilter" to "Last training", + "members.lastTrainingFilterHasDate" to "With a recorded date", + "members.lastTrainingFilterHint" to "Age-class column: current and next season. Extra detail (last training, participations) on row hover.", + "members.lastTrainingFilterNoDate" to "No last training", + "members.lastTrainingFilterNotInTraining" to "Flagged “no longer training”", + "members.loadingMembers" to "Loading members...", + "members.m11" to "G11", + "members.m13" to "G13", + "members.m15" to "G15", + "members.m19" to "G19", + "members.m9" to "G9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "Member deactivated", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "Membership form handed over", + "members.memberImage" to "Member image", + "members.memberImages" to "Member images", + "members.memberInfo" to "Member info", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "No myTischtennis ID stored", + "members.name" to "Surname, first name", + "members.newMember" to "New member", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "No groups assigned", + "members.noGroupsAvailable" to "No groups available", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "Notes", + "members.noTestMembership" to "No longer a trial member", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "Only active members are included", + "members.openTasks" to "Open tasks", + "members.parent" to "Parent", + "members.parentFallback" to "Parent", + "members.parentName" to "Name (e.g. mother, father)", + "members.phoneList" to "phone-list.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "Phone number", + "members.phoneNumberShort" to "Phone no.", + "members.phones" to "Phone numbers", + "members.picsInInternetAllowed" to "Pictures allowed online", + "members.postalCode" to "Postcode", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "Primary", + "members.primaryImageUpdated" to "Primary image updated.", + "members.remove" to "Remove", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "Active + data incomplete", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "Data incomplete", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "Search and filter", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "Select file", + "members.showInactiveMembers" to "Show inactive members", + "members.showTrainingParticipationsColumn" to "Show “Training participations” column", + "members.showTtrHistory" to "Show TTR history", + "members.sixOrMoreParticipations" to "6 or more training participations", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "QTTR value", + "members.status" to "Status", + "members.street" to "Street", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "Open", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "Assign training group", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "Review data quality", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "Trial", + "members.testMembers" to "Trial members", + "members.testMembership" to "Trial membership", + "members.testMembershipRemoved" to "Trial membership removed.", + "members.threeOrMoreParticipations" to "3 or more training participations", + "members.title" to "Members", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "Training groups", + "members.trainingParticipations" to "Training participations", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "Transfer members", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "Adults (not youth by cutoff)", + "members.ttAgeClassCol" to "Age class (TT)", + "members.ttFilterGroupJ" to "Boys & girls (mixed)", + "members.ttFilterGroupM" to "Girls only", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "current", + "members.ttSeasonFilter" to "Season (cutoff)", + "members.ttSeasonNextTag" to "upcoming", + "members.ttStichtagHint" to "Cutoff 1 Jan (DTTB). Boys: J classes only. Girls: J and M.", + "members.updateRatings" to "Update TTR/QTTR from myTischtennis", + "members.updating" to "Updating...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "Wants to play", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Cancel", + "messages.confirm" to "Confirm", + "messages.error" to "Error", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Success", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Warning", + "mobile.active" to "Active", + "mobile.add" to "Add", + "mobile.appLoading" to "Loading app", + "mobile.cancel" to "Cancel", + "mobile.clubRequest" to "Request access", + "mobile.clubRequested" to "Requested", + "mobile.createDiaryEntry" to "Create entry", + "mobile.deleteNote" to "Delete note", + "mobile.deleteTag" to "Remove tag", + "mobile.editTimes" to "Edit times", + "mobile.emailAndPasswordRequired" to "Email and password are required", + "mobile.entry" to "Entry", + "mobile.inactive" to "Inactive", + "mobile.language" to "Language", + "mobile.lastTraining" to "Last training", + "mobile.loginFailed" to "Login failed", + "mobile.loginInProgress" to "Signing in...", + "mobile.more" to "More", + "mobile.new" to "New", + "mobile.newNote" to "New note", + "mobile.newTag" to "New tag", + "mobile.noDiaryEntries" to "No diary entries yet", + "mobile.noMembers" to "No members found", + "mobile.noResults" to "No results", + "mobile.noTimes" to "No times", + "mobile.participationTop" to "Top attendance", + "mobile.refresh" to "Refresh", + "mobile.requestedAccess" to "Requested", + "mobile.role" to "Role", + "mobile.saveEntry" to "Save", + "mobile.search" to "Search", + "mobile.select" to "Select", + "mobile.sessionCheck" to "Check session", + "mobile.sessionCheckFailed" to "Session check failed", + "mobile.sessionInvalid" to "Session invalid", + "mobile.sessionValid" to "Session valid", + "mobile.status" to "Status", + "mobile.user" to "User", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Approvals", + "navigation.backToHome" to "Back to home", + "navigation.billing" to "Billing", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Club Settings", + "navigation.clubTournaments" to "Club Tournaments", + "navigation.competitions" to "Competitions", + "navigation.dailyBusiness" to "Daily Business", + "navigation.diary" to "Diary", + "navigation.home" to "Home", + "navigation.login" to "Login", + "navigation.logout" to "Logout", + "navigation.logs" to "System Logs", + "navigation.members" to "Members", + "navigation.memberTransfer" to "Member Transfer", + "navigation.myTischtennisAccount" to "myTischtennis Account", + "navigation.orders" to "Orders", + "navigation.permissions" to "Permissions", + "navigation.personalSettings" to "Personal Settings", + "navigation.predefinedActivities" to "Predefined Activities", + "navigation.register" to "Register", + "navigation.schedule" to "Schedules", + "navigation.settings" to "Settings", + "navigation.statistics" to "Training Statistics", + "navigation.teamManagement" to "Team Management", + "navigation.tournamentParticipations" to "Tournament Participations", + "navigation.tournaments" to "Tournaments", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Create order", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Cost", + "orders.dateAutoHint" to "The date is set automatically and every change is logged with a date.", + "orders.errorLoading" to "Orders could not be loaded.", + "orders.errorSaving" to "Order could not be saved.", + "orders.filterAllClubs" to "All clubs", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "All statuses", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "All orders across clubs can be viewed and managed here.", + "orders.globalTitle" to "Orders Across All Clubs", + "orders.history" to "History", + "orders.item" to "Item", + "orders.itemPlaceholder" to "e.g. shirt, hoodie or bat cover", + "orders.loading" to "Loading orders...", + "orders.member" to "Member", + "orders.memberTitle" to "Orders: {name}", + "orders.noOrdersGlobal" to "There are currently no orders.", + "orders.noOrdersMember" to "There are no orders for this member yet.", + "orders.open" to "Outstanding", + "orders.orderDate" to "Created on", + "orders.paid" to "Paid", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Search by club, member or item", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "item arrived", + "orders.statusDate" to "Last change", + "orders.statusHandedOver" to "item handed over", + "orders.statusOrdered" to "ordered", + "orders.statusRequested" to "requested", + "orders.title" to "Orders", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Generated:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "League:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Registration for:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Planned league:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Season:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Team age group:", + "pdfGenerator.teamGenderLabel" to "Team gender:", + "pdfGenerator.teamLineupTitle" to "Team line-up", + "pdfGenerator.teamNameLabel" to "Team:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Exclude from statistics", + "predefinedActivities.filterAll" to "All", + "predefinedActivities.filterCustom" to "Custom", + "predefinedActivities.filterStandard" to "Standard activities", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Active selection", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Adult schedule", + "schedule.ageClass" to "Age group", + "schedule.allLeagueMatches" to "All matches", + "schedule.balls" to "Balls", + "schedule.cancel" to "Cancel", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Completed", + "schedule.copyCode" to "Copy code", + "schedule.copyGuestPin" to "Copy away PIN", + "schedule.copyHomePin" to "Copy home PIN", + "schedule.date" to "Date", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Error while copying", + "schedule.errorImportingCSV" to "Failed to import the CSV file", + "schedule.errorLoadingAdultSchedule" to "Failed to load the adult schedule", + "schedule.errorLoadingGallery" to "The gallery could not be loaded.", + "schedule.errorLoadingMatches" to "Failed to load matches", + "schedule.errorLoadingMembers" to "Failed to load the member list.", + "schedule.errorLoadingOverallSchedule" to "Failed to load the overall schedule", + "schedule.errorLoadingTable" to "Failed to load league table data from myTischtennis", + "schedule.errorLoadingTeams" to "Failed to load teams", + "schedule.errorSavingPlayerSelection" to "Failed to save the player selection", + "schedule.fetchDataFailed" to "Team data could not be fetched.", + "schedule.fetchingTeamData" to "Fetching…", + "schedule.fetchStartFailed" to "The fetch could not be started.", + "schedule.fetchTeamData" to "Fetch schedule & results", + "schedule.fetchTimedOut" to "Timed out while fetching data.", + "schedule.gallery" to "Member gallery", + "schedule.galleryLoading" to "Loading gallery…", + "schedule.galleryTitle" to "Member gallery - click an image to mark as ready", + "schedule.gamesFor" to "Matches for", + "schedule.guestPin" to "Away PIN", + "schedule.guestTeam" to "Away team", + "schedule.homePin" to "Home PIN", + "schedule.homeTeam" to "Home team", + "schedule.imageSize" to "Image size", + "schedule.importSchedule" to "Import schedule", + "schedule.leagueTable" to "League table", + "schedule.loadingMembers" to "Loading members...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Controls which matches from the currently selected league are shown in the schedule.", + "schedule.matchOverviewTitle" to "Match overview", + "schedule.nextMatch" to "Next match", + "schedule.noActiveMembers" to "No active members found", + "schedule.noGames" to "No matches available", + "schedule.noLeagueForTeam" to "No league is assigned to this team. Please assign a league first.", + "schedule.noMatchesForPDF" to "No matches found to generate a PDF.", + "schedule.noMatchingTeams" to "No teams match the current search.", + "schedule.noMembersWithImages" to "No members with images found.", + "schedule.noSelectionMessage" to "Choose the overall schedule, the adult schedule or a team from the left.", + "schedule.noSelectionTitle" to "No active selection yet", + "schedule.noTableData" to "No table data available", + "schedule.noTeamsFound" to "No teams found for this season", + "schedule.openMatchReport" to "Open match report", + "schedule.otherTeamMatches" to "Other team", + "schedule.overallSchedule" to "Overall schedule", + "schedule.ownTeamMatches" to "Own team", + "schedule.pendingShort" to "Open", + "schedule.planned" to "Planned", + "schedule.played" to "Played", + "schedule.player" to "Player", + "schedule.playerSelection" to "Player selection", + "schedule.playerSelectionSaved" to "Player selection saved", + "schedule.pleaseSelectGame" to "Please select a match first", + "schedule.points" to "Pts", + "schedule.position" to "Position", + "schedule.ready" to "Ready", + "schedule.refreshTable" to "Refresh table", + "schedule.result" to "Result", + "schedule.save" to "Save", + "schedule.scheduleImportSuccess" to "Schedule imported successfully.", + "schedule.schedulePDF" to "Schedules.pdf", + "schedule.scheduleTab" to "Schedule", + "schedule.searchTeams" to "Search team or league", + "schedule.selection" to "Selection", + "schedule.selectOtherTeam" to "Select other team", + "schedule.selectSpecificLeague" to "Please select a specific league to load the table.", + "schedule.sets" to "Sets", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Select teams, review fixtures and keep league tables in view.", + "schedule.tableDataLoaded" to "League table data loaded successfully from myTischtennis.", + "schedule.tableLoading" to "Loading table…", + "schedule.tableTab" to "Table", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Team data updated successfully.", + "schedule.teamDataFetchedDetails" to "Team: {team}\nProcessed records: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Time", + "schedule.title" to "Schedules", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} matches, {completed} completed and {pending} open.", + "schedule.workspaceTableDescription" to "{count} table rows currently loaded.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Language", + "settings.languageChanged" to "Language successfully changed", + "settings.languageDescription" to "Choose your preferred language for the application", + "settings.personalSettings" to "Personal Settings", + "settings.selectLanguage" to "Select Language", + "settings.title" to "Settings", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Active team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Please assign a league to the team first so documents can be processed.", + "teamManagement.assignLeagueBeforeParsing" to "Please assign a league to the team first to parse PDF files.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Association", + "teamManagement.asyncJobStartFailed" to "The async job could not be started.", + "teamManagement.autoFetchEnabled" to "Automatic data retrieval is enabled", + "teamManagement.automaticJobs" to "Automatic jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Available players", + "teamManagement.basicSettings" to "Basic settings", + "teamManagement.basicSettingsIntro" to "Review and adjust team name and league in a calmer form.", + "teamManagement.change" to "Change", + "teamManagement.clearFields" to "Clear fields", + "teamManagement.codeList" to "Code list", + "teamManagement.configurationStatus" to "Configuration status", + "teamManagement.configureLeagueDetails" to "Association: {association}\nSeason: {season}\nLeague: {league}\nGroup ID: {groupId}\n\nDo you want to configure this league in the database? This enables automatic table data retrieval.", + "teamManagement.configureLeagueTitle" to "Configure league?", + "teamManagement.createAndEdit" to "Create & edit", + "teamManagement.created" to "Created", + "teamManagement.createNewTeam" to "Create new team", + "teamManagement.dataFetchFailed" to "Data could not be fetched.", + "teamManagement.delete" to "Delete", + "teamManagement.deleteTeamConfirm" to "Do you really want to delete the club team \"{name}\"?", + "teamManagement.deleteTeamTitle" to "Delete club team", + "teamManagement.documentAvailable" to "Available", + "teamManagement.documentMissing" to "Missing", + "teamManagement.documentNotFound" to "The selected document could not be found.", + "teamManagement.documentParsedSummary" to "{label} uploaded and parsed successfully.\n\nMatches found: {matchesFound}\nNew matches created: {created}\nMatches updated: {updated}", + "teamManagement.documents" to "Documents", + "teamManagement.documentsIntro" to "Upload, review and re-parse code lists and PIN lists when needed.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" was uploaded successfully.", + "teamManagement.edit" to "Edit", + "teamManagement.editTeam" to "Edit team", + "teamManagement.eligibility" to "Eligibility", + "teamManagement.eligibilityAdultRelease" to "Approved for adults", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Approved + adult reserve", + "teamManagement.eligibilityAdultReserve" to "Adult reserve", + "teamManagement.eligibilityRegular" to "Regular", + "teamManagement.enterUrlForAutoConfig" to "Enter a myTischtennis URL for automatic configuration", + "teamManagement.error" to "Error", + "teamManagement.errorConfiguringLeague" to "The league could not be configured.", + "teamManagement.errorConfiguringTeam" to "The team could not be configured.", + "teamManagement.errorDeletingTeam" to "Failed to delete the club team.", + "teamManagement.errorLoadingPdf" to "Failed to load the PDF.", + "teamManagement.errorLoadingStats" to "Statistics could not be loaded.", + "teamManagement.errorParsingPdf" to "Failed to parse the PDF file", + "teamManagement.errorParsingUrl" to "The URL could not be parsed. Please check the format.", + "teamManagement.errorsCount" to "Errors: {count}", + "teamManagement.errorUploadingDocument" to "Failed to upload and parse the file.", + "teamManagement.exportLineupPdf" to "Export line-up as PDF", + "teamManagement.fetched" to "fetched", + "teamManagement.fetchTimedOut" to "Timed out while fetching data (async job took too long).", + "teamManagement.fetchTimeoutShort" to "Timed out while fetching data.", + "teamManagement.fileTooLarge" to "{label} must not exceed 10 MB.", + "teamManagement.fileTooLargeTitle" to "File too large", + "teamManagement.filterAll" to "All", + "teamManagement.filterConfigured" to "Configured", + "teamManagement.filterNeedsAttention" to "Needs review", + "teamManagement.filterNoLeague" to "Without league", + "teamManagement.firstHalf" to "First half", + "teamManagement.firstHalfFull" to "First half (July - December)", + "teamManagement.fullyConfigured" to "Fully configured", + "teamManagement.groupId" to "Group ID", + "teamManagement.groupName" to "Group name", + "teamManagement.invalidFileExtension" to "{label} must have one of the following extensions: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Invalid file type", + "teamManagement.invalidMimeType" to "{label} has an unexpected MIME type: {type}.", + "teamManagement.lastRun" to "Last run", + "teamManagement.lastUpdated" to "Last updated", + "teamManagement.latestUpload" to "Last uploaded: {date}", + "teamManagement.league" to "League", + "teamManagement.leagueConfiguredDetails" to "League: {league}\nSeason: {season}\nAssociation: {association}\nGroup ID: {groupId}\n\nTable data can now be retrieved automatically.", + "teamManagement.leagueConfiguredSuccess" to "League configured successfully. Table data can now be retrieved automatically.", + "teamManagement.leagueFieldRequired" to "Please select a league class and save.", + "teamManagement.lineupEmpty" to "No players have been assigned to this team yet.", + "teamManagement.lineupPdfEmpty" to "No players in the line-up – cannot create PDF.", + "teamManagement.lineupPdfFilePrefix" to "Line-up", + "teamManagement.lineupProposal" to "Line-up by QTTR", + "teamManagement.lineupSaved" to "Team line-up saved.", + "teamManagement.lineupSaveError" to "Team line-up could not be saved.", + "teamManagement.lineupUnsavedChanges" to "There are unsaved changes in the team line-up.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} is more than 30 QTTR points ahead of {lower}. Please correct this order.", + "teamManagement.loadingStats" to "Loading statistics...", + "teamManagement.manualFetch" to "Fetch", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Matches found: {matchesFound}\nNew matches created: {created}\nMatches updated: {updated}", + "teamManagement.matchResults" to "Match results", + "teamManagement.missingConfigSummary" to "Missing:", + "teamManagement.missingItems" to "Missing: {items}", + "teamManagement.missingLeagueForTeam" to "No league was provided for the selected team.", + "teamManagement.moreErrors" to "... and {count} more", + "teamManagement.myTischtennis" to "myTischtennis", + "teamManagement.myTischtennisIntro" to "Paste a URL, review the configuration and fetch data manually when needed.", + "teamManagement.mytischtennisLoginRequired" to "Login to myTischtennis required", + "teamManagement.myTischtennisUrlPlaceholder" to "myTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Paste and parse the MyTischtennis URL to link the team ID and league data.", + "teamManagement.never" to "Never", + "teamManagement.newTeam" to "New team", + "teamManagement.noAssignment" to "No assignment", + "teamManagement.noAutomaticUpdate" to "No automatic update yet", + "teamManagement.noDocumentUploadYet" to "No document uploaded yet.", + "teamManagement.noIssues" to "No open configuration issues.", + "teamManagement.noLeague" to "No league", + "teamManagement.noMatchesFoundDetails" to "Note: No matches detected.\nLines in document: {lines}", + "teamManagement.noMatchesFoundTitle" to "No matches found", + "teamManagement.noMatchingTeams" to "No teams match the current search or filter.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "No appearances recorded.", + "teamManagement.notConfigured" to "Not configured", + "teamManagement.notCreated" to "Not created", + "teamManagement.noTeamsYet" to "No teams yet. Create your first team.", + "teamManagement.openDocument" to "Open", + "teamManagement.openInWorkspace" to "Open to edit", + "teamManagement.parseDocument" to "Parse again", + "teamManagement.parseUrlAction" to "Check URL", + "teamManagement.partiallyConfigured" to "Partially configured", + "teamManagement.pdfFileNotFound" to "The PDF file could not be found.", + "teamManagement.pinList" to "PIN list", + "teamManagement.plannedLeague" to "Planned league", + "teamManagement.plannedLeagueHint" to "Optional. Independent of the registered league (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "e.g. district league, after next registration …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Player", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Appearances", + "teamManagement.playerStatsIntro" to "Quick overview of this team's appearances in the current season.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR rating", + "teamManagement.ratingUpdates" to "Rating updates", + "teamManagement.refreshStats" to "Refresh", + "teamManagement.reuploadFile" to "Please upload the file again and try once more.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Search team", + "teamManagement.season" to "Season", + "teamManagement.seasonFull" to "Full season (from 1 July)", + "teamManagement.seasonUnknown" to "unknown", + "teamManagement.secondHalf" to "Second half", + "teamManagement.secondHalfFull" to "Second half (from 1 January)", + "teamManagement.selectedLineup" to "Selected players", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Please select a team first", + "teamManagement.selectTeamForConfiguration" to "To activate the myTischtennis configuration, you must first select a team from the list.", + "teamManagement.selectTeamTitle" to "Select team", + "teamManagement.showCodeList" to "Show code list", + "teamManagement.showPinList" to "Show PIN list", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Select teams, review configuration and maintain league data in one place.", + "teamManagement.successful" to "Successful", + "teamManagement.tableUpdateLabel" to "Table update:", + "teamManagement.tableUrlDetected" to "Table URL detected", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Age group", + "teamManagement.teamConfiguredDetails" to "League: {league}\nSeason: {season}\nAutomatic data retrieval is now active.", + "teamManagement.teamConfiguredSuccess" to "Team configured successfully. Automatic data retrieval is now active.", + "teamManagement.teamDataFetched" to "Team data fetched successfully.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nFetched records: {count}", + "teamManagement.teamGender" to "Gender", + "teamManagement.teamGenderFemale" to "Female", + "teamManagement.teamGenderOpen" to "Open", + "teamManagement.teamHasNoLeague" to "This team is not assigned to a league.", + "teamManagement.teamId" to "Team ID", + "teamManagement.teamName" to "Team name", + "teamManagement.teamNamePlaceholder" to "e.g. Men 1, Women 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team Management", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unknown", + "teamManagement.unknownTeam" to "Unknown team", + "teamManagement.updated" to "updated", + "teamManagement.uploadNewVersion" to "Upload new version", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Add pool rule", + "tournaments.address" to "Address", + "tournaments.adjustTournamentSearch" to "Adjust the search term or create a new tournament.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "All participant data is complete.", + "tournaments.allDataCompleteTop3" to "All data for the top 3 placed players is complete.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Please add at least one pool rule for {label} (e.g. places 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Correct", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Create final round from intermediate round", + "tournaments.createFinalFromPreliminary" to "Create final round from preliminary round", + "tournaments.createGroupMatches" to "Create group matches", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Create intermediate round from preliminary round", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Not yet recorded", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Do you really want to delete class \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "All participants will be detached from this class.", + "tournaments.deleteClassTitle" to "Delete class", + "tournaments.deleteExistingPairingsConfirm" to "Existing pairings will be deleted. Continue?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Distribute free tables", + "tournaments.distributeTablesResult" to "Table distribution", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doubles tournament", + "tournaments.doublesTournamentHint" to "Switches all existing classes to doubles and creates new classes as doubles by default.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Suitable for: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Please enter a class name.", + "tournaments.enterExternalParticipantName" to "Please enter at least first name and last name.", + "tournaments.enterMiniLocation" to "Please enter a location.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Error distributing tables.", + "tournaments.errorGeneratingPdf" to "Error generating the PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Final round", + "tournaments.finishMatch" to "Finish", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Generating PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Groups", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Intermediate round (round 2)", + "tournaments.internalStatsAbsoluteRank" to "Total score ranking", + "tournaments.internalStatsAgeFilter" to "Age group & gender (singles)", + "tournaments.internalStatsAgeFilterAll" to "All age classes", + "tournaments.internalStatsAgeFilterNone" to "No age class selected", + "tournaments.internalStatsAgeNoClass" to "No class assigned", + "tournaments.internalStatsAgeSelectAll" to "All", + "tournaments.internalStatsAgeSelectNone" to "None", + "tournaments.internalStatsAverageRank" to "Average per tournament", + "tournaments.internalStatsAvgPoints" to "Avg.", + "tournaments.internalStatsEmpty" to "No data for the selected period.", + "tournaments.internalStatsExportPdf" to "Export as PDF", + "tournaments.internalStatsFilterAgeBands" to "Age classes", + "tournaments.internalStatsFilterGenderColumn" to "Gender", + "tournaments.internalStatsGenderScopeAll" to "All", + "tournaments.internalStatsGenderScopeGirls" to "Girls only", + "tournaments.internalStatsLast12Months" to "Last 12 months", + "tournaments.internalStatsLast3Months" to "Last 3 months", + "tournaments.internalStatsLast6Months" to "Last 6 months", + "tournaments.internalStatsOpenButton" to "Tournament statistics (singles)", + "tournaments.internalStatsPeriod" to "Period", + "tournaments.internalStatsPoints" to "Total", + "tournaments.internalStatsPointsExplain" to "Scoring: In each group, placement is expressed as a percentage (with N ranked players: 1st = 100%, last = 0%, linear in between; tied ranks share the same value). N counts everyone ranked in that group (including guests). With only one player: 100%. Players who reach the knockout get the highest group score in that class plus 1, then one extra point per knockout match won. Club members in singles classes only. Age bands (J9–J19 / adults) and gender (“All” = female and open channels, “Girls only” = female channel) use each member’s birth date and gender from club records, not the tournament class name.", + "tournaments.internalStatsTitle" to "Internal tournaments statistics (singles)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} tournament(s) in this period (excluding mini championships).", + "tournaments.internalStatsTtAdult" to "Adults", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Mark as live", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "At least 2 participants are required for pairings.", + "tournaments.missingDataPDF" to "Missing data as PDF", + "tournaments.missingDataPDFSubtitle" to "Please collect the missing data (marked with ____) from participants and note them here.", + "tournaments.missingDataPDFSubtitleTop3" to "Please collect missing data of the top 3 (marked with ____) and note them here.", + "tournaments.missingDataPDFTitle" to "Missing participant data – Mini championship", + "tournaments.missingDataPDFTitleTop3" to "Missing data – Top 3 Mini championship", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "New set, e.g. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "No matches available where both players are free.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "No suitable class found.", + "tournaments.noFreeTables" to "No free tables available.", + "tournaments.noMatchingTournamentsTitle" to "No matching tournaments", + "tournaments.noOrphanedMatchesFound" to "No orphaned matches found.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "No rules yet. Example: places 1 and 2 -> top round-2 groups.", + "tournaments.noPoolRulesYetFinal" to "No rules yet. Example: places 1 and 2 -> final round.", + "tournaments.noTop3Yet" to "No top 3 placements have been determined yet.", + "tournaments.noTournamentDate" to "No tournament date available.", + "tournaments.noTournamentsCreatedYet" to "No tournaments have been created yet.", + "tournaments.noTrainingOnDate" to "No training session found for {date}.", + "tournaments.noTrainingParticipants" to "No participants were found in the training session for this date.", + "tournaments.noValidTrainingParticipants" to "No valid participants were found in the training session for this date.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Number of tables", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} orphaned matches removed.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Page", + "tournaments.pairingAlreadyExists" to "This pairing already exists.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} pairings created, {errorCount} errors.", + "tournaments.participantConflicts" to "Conflicts", + "tournaments.participantNotFound" to "Participant not found.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} participants are not assigned to a class yet. These should be assigned first.", + "tournaments.phone" to "Phone", + "tournaments.placementsPendingFinals" to "Final placings will appear once multiple groups or a final round exist.", + "tournaments.placementsPendingGroups" to "Group placings will appear once group matches exist.", + "tournaments.placementsPendingNoGroups" to "Placings will appear here once group matches or a final round exist.", + "tournaments.placementsPendingSelectedClass" to "There are no placings for the currently selected class yet.", + "tournaments.placementsPendingTitle" to "Placings not available yet", + "tournaments.placesFromEachGroup" to "Places from each group (e.g. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Player 1", + "tournaments.playerTwo" to "Player 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Play third place match", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Advance: preliminary round → intermediate round (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Advance: round {from} → round {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Ranking", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Number of groups", + "tournaments.roundMode" to "Mode", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Save rounds", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Active tournament", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Please select a tournament first.", + "tournaments.selectTwoDifferentPlayers" to "Please select two different players.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} of {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Invalid server response.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "The intermediate round is optional. If you activate it, a final round follows afterwards. A knockout final round is created as a single bracket.", + "tournaments.stageConfigLoadError" to "Error loading round configuration.", + "tournaments.stageConfigLoading" to "Loading round configuration…", + "tournaments.stageConfigMissingIds" to "Cannot save: club or tournament ID is missing.", + "tournaments.stageConfigTitle" to "Intermediate & Final Round", + "tournaments.stageCreated" to "Round {round} was created.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Assign", + "tournaments.statusActionCreate" to "Create", + "tournaments.statusActionGenerate" to "Generate", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Review", + "tournaments.statusActionStart" to "Start", + "tournaments.statusConfigIncomplete" to "Configuration incomplete", + "tournaments.statusConfigReady" to "Configuration ready", + "tournaments.statusFinished" to "Finished", + "tournaments.statusGroupsMissing" to "Groups not created yet", + "tournaments.statusGroupsReady" to "Groups ready", + "tournaments.statusGroupsRunning" to "Group stage in progress", + "tournaments.statusKnockoutReady" to "Knockout ready to start", + "tournaments.statusKnockoutRunning" to "Knockout in progress", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Open", + "tournaments.statusParticipantsConflicts" to "{count} participants with conflicts", + "tournaments.statusParticipantsReady" to "{count} participants conflict-free", + "tournaments.statusParticipantsUnassigned" to "{count} without class", + "tournaments.statusTournamentCompleted" to "Tournament completed", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Table", + "tournaments.tablesDistributed" to "Tables have been distributed.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Target", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Target number of groups", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Female", + "tournaments.tournamentClassGenderOpen" to "Open (all)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Remove live marker", + "tournaments.unpairedDoubles" to "Doubles without partner", + "tournaments.useIntermediateStage" to "Use intermediate round", + "tournaments.warningBirthDateMissing" to "Birth date is missing for this class", + "tournaments.warningClassMissing" to "Class not found", + "tournaments.warningGenderMismatch" to "Gender does not match class", + "tournaments.warningGenderMissing" to "Gender is missing for this class", + "tournaments.warningMissingPairing" to "Doubles partner missing", + "tournaments.warningTooOldForClass" to "Too old for this class", + "tournaments.warningTooYoungForClass" to "Too young for this class", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Actions", + "trainingStats.activeMembers" to "Active members", + "trainingStats.allTrainingDays" to "All training days", + "trainingStats.attendingMembers" to "Attending members", + "trainingStats.averageParticipationCurrentMonth" to "Average participation (current month)", + "trainingStats.averageParticipationHalfYear" to "Average participation (half-year)", + "trainingStats.averageParticipationLastMonth" to "Average participation (last month)", + "trainingStats.averageParticipationQuarter" to "Average participation (quarter)", + "trainingStats.averageParticipationYear" to "Average participation (year)", + "trainingStats.birthdate" to "Birthdate", + "trainingStats.date" to "Date", + "trainingStats.lastTraining" to "Last training", + "trainingStats.memberParticipations" to "Member participations", + "trainingStats.name" to "Name", + "trainingStats.noParticipants" to "No participants", + "trainingStats.participants" to "Participants", + "trainingStats.participations12Months" to "Participations (12 months)", + "trainingStats.participations3Months" to "Participations (3 months)", + "trainingStats.participationsTotal" to "Participations (total)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Show details", + "trainingStats.title" to "Training statistics", + "trainingStats.trainingDayFilter" to "Training day", + "trainingStats.trainingDays" to "Training days (last 12 months)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Weekday", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val en_US: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Training Diary", + "app.title" to "Training Diary", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Activation failed. Please check the link or try again.", + "auth.confirmPassword" to "Confirm password", + "auth.email" to "Email", + "auth.forgotPassword" to "Forgot password?", + "auth.forgotPasswordDescription" to "Enter your email address. You will receive a link to reset your password.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Log In", + "auth.loginFailed" to "Login failed. Please check your credentials and try again.", + "auth.loginSuccess" to "Successfully logged in", + "auth.logout" to "Log Out", + "auth.logoutSuccess" to "Successfully logged out", + "auth.newPassword" to "New password", + "auth.noAccount" to "Don't have an account?", + "auth.password" to "Password", + "auth.passwordResetSuccess" to "Your password has been changed successfully. You can now log in with your new password.", + "auth.passwordsDoNotMatch" to "Passwords do not match.", + "auth.passwordTooShort" to "Password must be at least 6 characters long.", + "auth.register" to "Sign Up", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Remember me", + "auth.resetEmailSent" to "If an account with this email exists, a reset link has been sent. Please check your inbox (and spam folder).", + "auth.resetFailed" to "Password could not be changed. The link may have expired.", + "auth.resetPassword" to "Set new password", + "auth.resetRequestFailed" to "Request failed. Please try again.", + "auth.saveNewPassword" to "Save password", + "auth.saving" to "Saving...", + "auth.sending" to "Sending...", + "auth.sendResetLink" to "Send link", + "auth.sessionExpired" to "Your session has expired. You will be logged out.", + "auth.toLogin" to "Go to login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Access to the club was denied.", + "club.accessRequested" to "Access has been requested.", + "club.accessRequestFailed" to "Access request could not be submitted.", + "club.accessRequestPending" to "Access to this club has been requested. Please be patient.", + "club.create" to "Create Club", + "club.createTitle" to "Create club", + "club.diary" to "Training diary", + "club.errorLoadingRequests" to "Failed to load open requests", + "club.load" to "Load", + "club.members" to "Members", + "club.mobileSelectHint" to "Please select a club first to use the app on your smartphone.", + "club.name" to "Club Name", + "club.new" to "New Club", + "club.noAccess" to "You have not yet been granted access to this club.", + "club.openAccessRequests" to "Open access requests", + "club.openRequests" to "Open access requests", + "club.requestAccess" to "Request access", + "club.select" to "Select Club", + "club.selectPlaceholder" to "Choose club...", + "club.title" to "Club", + "club.trainingDiary" to "Training diary", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Actions", + "common.active" to "Active", + "common.add" to "Add", + "common.all" to "All", + "common.apply" to "Apply", + "common.back" to "Back", + "common.cancel" to "Cancel", + "common.choose" to "Choose", + "common.clear" to "Clear", + "common.close" to "Close", + "common.confirm" to "Confirm", + "common.create" to "Create", + "common.date" to "Date", + "common.days" to "days", + "common.delete" to "Delete", + "common.description" to "Description", + "common.details" to "Details", + "common.disabled" to "Disabled", + "common.download" to "Download", + "common.edit" to "Edit", + "common.enabled" to "Enabled", + "common.filter" to "Filter", + "common.hours" to "hours", + "common.in" to "in", + "common.inactive" to "Inactive", + "common.loading" to "Loading...", + "common.min" to "min", + "common.minutes" to "minutes", + "common.months" to "months", + "common.move" to "Move", + "common.name" to "Name", + "common.new" to "New", + "common.next" to "Next", + "common.no" to "No", + "common.ok" to "OK", + "common.optional" to "Optional", + "common.period" to "Period", + "common.previous" to "Previous", + "common.refresh" to "Reload", + "common.remove" to "Remove", + "common.required" to "Required", + "common.reset" to "Reset", + "common.save" to "Save", + "common.saved" to "Saved", + "common.saving" to "Saving...", + "common.search" to "Search", + "common.select" to "Select", + "common.status" to "Status", + "common.submit" to "Submit", + "common.time" to "Time", + "common.today" to "Today", + "common.type" to "Type", + "common.update" to "Update", + "common.view" to "View", + "common.weeks" to "weeks", + "common.years" to "years", + "common.yes" to "Yes", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Add stroke", + "courtDrawingTool.backhand" to "Backhand", + "courtDrawingTool.codeLabel" to "Code", + "courtDrawingTool.completeFirstStroke" to "Complete the first stroke", + "courtDrawingTool.completeFirstStrokeHint" to "Choose start position, stroke side, spin and target. The graphic will then appear.", + "courtDrawingTool.configureExercise" to "Configure exercise", + "courtDrawingTool.counterSpin" to "Counter-spin", + "courtDrawingTool.forehand" to "Forehand", + "courtDrawingTool.noAdditionalStrokes" to "No follow-up strokes added yet.", + "courtDrawingTool.preview" to "Preview", + "courtDrawingTool.previewHint" to "The graphic appears as soon as the first stroke is fully configured.", + "courtDrawingTool.service" to "Serve:", + "courtDrawingTool.serviceTitle" to "Serve", + "courtDrawingTool.sidespin" to "Sidespin", + "courtDrawingTool.sideUnderspin" to "Side-backspin", + "courtDrawingTool.spin" to "Spin:", + "courtDrawingTool.startLeft" to "left", + "courtDrawingTool.startMiddle" to "center", + "courtDrawingTool.startRight" to "right", + "courtDrawingTool.stepAdditionalStrokes" to "4. Follow-up strokes", + "courtDrawingTool.stepAdditionalStrokesHint" to "Optionally build additional balls as a sequence.", + "courtDrawingTool.stepFirstStroke" to "2. First stroke", + "courtDrawingTool.stepFirstStrokeHint" to "Set stroke side and spin for the first ball.", + "courtDrawingTool.stepStartPosition" to "1. Start position", + "courtDrawingTool.stepStartPositionHint" to "Which service position does the exercise start from?", + "courtDrawingTool.stepTarget" to "3. Target", + "courtDrawingTool.stepTargetHint" to "Choose the target zone for the first stroke.", + "courtDrawingTool.strokeSide" to "Side", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Chop defense", + "courtDrawingTool.strokeTypeCounter" to "Counter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Stroke type", + "courtDrawingTool.strokeTypeLobDefense" to "Lob defense", + "courtDrawingTool.strokeTypePush" to "Push", + "courtDrawingTool.strokeTypeSmash" to "Smash", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Backhand half-long", + "courtDrawingTool.targetBackhandLong" to "Backhand deep", + "courtDrawingTool.targetBackhandShort" to "Backhand short", + "courtDrawingTool.targetForehandHalfLong" to "Forehand half-long", + "courtDrawingTool.targetForehandLong" to "Forehand deep", + "courtDrawingTool.targetForehandShort" to "Forehand short", + "courtDrawingTool.targetMiddleHalfLong" to "Middle half-long", + "courtDrawingTool.targetMiddleLong" to "Middle deep", + "courtDrawingTool.targetMiddleShort" to "Middle short", + "courtDrawingTool.targetPosition" to "Target position:", + "courtDrawingTool.targetPositionLabel" to "Target position", + "courtDrawingTool.title" to "Table tennis exercise drawing", + "courtDrawingTool.titleLabel" to "Title", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "to", + "courtDrawingTool.underspin" to "Backspin", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Active training day", + "diary.activities" to "Activities", + "diary.activity" to "Activity", + "diary.activityDrawing" to "Activity drawing", + "diary.activityImage" to "Activity image", + "diary.activityNotFound" to "Activity not found. Please choose one from the list.", + "diary.activityOrTimeblock" to "Activity / time block", + "diary.activityPlaceholder" to "Activity", + "diary.activityRequired" to "Please enter an activity.", + "diary.addActivity" to "Add activity", + "diary.addGroup" to "Add group", + "diary.addGroupActivity" to "Add group activity", + "diary.addGroupButton" to "+ Group", + "diary.addTimeblock" to "Time block", + "diary.all" to "All", + "diary.applySuggestion" to "Apply suggestion", + "diary.assignParticipants" to "Assign participants", + "diary.assignParticipantsForGroupActivity" to "Assign participants for group activity", + "diary.assignShort" to "Assign", + "diary.bookAccident" to "Record accident", + "diary.confirmDelete" to "Confirm deletion", + "diary.confirmDeleteDate" to "Do you really want to delete this date?", + "diary.confirmDeleteDateDetails" to "All associated data will also be deleted.", + "diary.confirmDeleteGroup" to "Do you really want to delete the group \"{name}\"?", + "diary.createDate" to "Create date", + "diary.createDrawing" to "Create exercise drawing", + "diary.createGroups" to "Create groups", + "diary.createNew" to "Create new", + "diary.createNewDate" to "Create new date", + "diary.date" to "Date", + "diary.dateCannotBeDeleted" to "Date cannot be deleted", + "diary.dateCannotBeDeletedDetails" to "There is still content present (training plan, participants, activities, accidents or notes).", + "diary.dateNoLongerCurrent" to "The selected date was no longer current. Please try again.", + "diary.delete" to "Delete", + "diary.deleteDate" to "Delete date", + "diary.deleteGroup" to "Delete group", + "diary.duration" to "Duration", + "diary.durationExampleLong" to "e.g. 2x7 or 3*5", + "diary.durationExampleShort" to "e.g. 2x7", + "diary.durationMinutes" to "Duration (min)", + "diary.editActivity" to "Edit activity", + "diary.editGroupActivity" to "Edit group activity", + "diary.editTrainingTimes" to "Edit training times", + "diary.errorCreatingActivity" to "Error creating activity", + "diary.errorCreatingGroups" to "Error creating groups", + "diary.errorDeletingGroup" to "Error deleting group", + "diary.errorLoadingPredefinedActivities" to "Failed to load predefined activities", + "diary.errorMarkingForm" to "Error marking the membership form", + "diary.errorOccurred" to "An error occurred. Please try again.", + "diary.existingGroups" to "Existing groups", + "diary.filterAbsent" to "Absent", + "diary.filterAll" to "All", + "diary.filterExcused" to "Excused", + "diary.filterPresent" to "Present", + "diary.filterTest" to "Trial", + "diary.formHandedOver" to "Membership form handed over", + "diary.formMarkedAsHandedOver" to "Membership form marked as handed over", + "diary.freeActivities" to "Free activities", + "diary.gallery" to "Member gallery", + "diary.galleryCreating" to "Creating gallery…", + "diary.group" to "Group...", + "diary.groupDeletedSuccessfully" to "Group deleted successfully.", + "diary.groupManagement" to "Group management", + "diary.groupsCreated" to "{count} groups were created successfully.", + "diary.groupsLabel" to "Groups", + "diary.groupsSection" to "Groups", + "diary.leader" to "Leader", + "diary.min" to "Min", + "diary.minutes" to "Minutes", + "diary.mustCreateAtLeastTwoGroups" to "At least 2 groups must be created the first time.", + "diary.nextAppointment" to "Next appointment", + "diary.noActiveTrainingDay" to "No training day selected.", + "diary.noEntries" to "No entries", + "diary.noFreeActivitiesYet" to "No free activities have been recorded yet.", + "diary.noParticipants" to "No participants available for this training day.", + "diary.numberOfGroups" to "Number of groups", + "diary.oneGroupAdded" to "1 group was added successfully.", + "diary.openPlanItems" to "{count} open", + "diary.openPlanItemsLabel" to "Plan status", + "diary.overallActivity" to "Overall activity", + "diary.participants" to "Participants", + "diary.participantStatusCancelled" to "Cancelled", + "diary.participantStatusExcused" to "Excused", + "diary.participantStatusNone" to "No status", + "diary.planActivitiesCount" to "Planned activities", + "diary.planAddHint" to "Add new plan items using the actions above.", + "diary.planEmptyState" to "Nothing has been entered in the training plan yet.", + "diary.quickAdd" to "+ Quick add", + "diary.searchParticipants" to "Search participants", + "diary.selectGroup" to "Select group...", + "diary.selectGroupAndActivity" to "Please select a group and enter an activity.", + "diary.selectParticipantAndNote" to "Please select a participant and enter a note.", + "diary.selectTags" to "Select tags", + "diary.selectTrainingGroup" to "Select training group", + "diary.selectTrainingGroupPlaceholder" to "Please choose...", + "diary.showImage" to "Show image/drawing", + "diary.skipSuggestion" to "Continue without suggestion", + "diary.standardActivities" to "Standard activities", + "diary.standardActivityAddError" to "Standard activity could not be added.", + "diary.standardDurationShort" to "Min", + "diary.startTime" to "Start time", + "diary.statusEmpty" to "This training day is still empty.", + "diary.statusInProgress" to "This training day is partially prepared.", + "diary.statusOpenShort" to "Open", + "diary.statusReady" to "Times and training plan are set.", + "diary.statusReadyShort" to "Ready", + "diary.suggestion" to "Suggestion", + "diary.timeblock" to "Time block", + "diary.timeblocksCount" to "Time blocks", + "diary.title" to "Training diary", + "diary.today" to "Today", + "diary.trainingDayAsPDF" to "Download training day as PDF", + "diary.trainingDayAsPDFShort" to "Training day as PDF", + "diary.trainingDayChecklist" to "Completion checklist", + "diary.trainingDaySection" to "Training day", + "diary.trainingDaySummaryPdfShort" to "Participant summary as PDF", + "diary.trainingEnd" to "Training end", + "diary.trainingPlan" to "Training plan", + "diary.trainingPlanAsPDF" to "Training plan as PDF", + "diary.trainingPlanPdfShort" to "Schedule as PDF", + "diary.trainingStart" to "Training start", + "diary.trainingTimesUpdated" to "Training times updated successfully.", + "diary.trainingWindow" to "Training window", + "diary.trainingWindowUnset" to "Not set yet", + "diary.unassignedPlanItems" to "{count} open", + "diary.updateTimes" to "Update times", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (German)", + "languages.de-CH" to "Schwiizerdütsch (Swiss German)", + "languages.en-AU" to "English (Australian English)", + "languages.en-GB" to "English (British English)", + "languages.en-US" to "English", + "languages.es" to "Español (Spanish)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (French)", + "languages.it" to "Italiano (Italian)", + "languages.ja" to "日本語 (Japanese)", + "languages.pl" to "Polski (Polish)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Chinese)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Actions", + "members.active" to "Active", + "members.activeMembers" to "Active members", + "members.addEmail" to "Add email address", + "members.addGroup" to "Add group...", + "members.addPhone" to "Add phone number", + "members.address" to "Address", + "members.adultReleaseApproved" to "Approved for adults", + "members.adultReserveApproved" to "Adult reserve", + "members.adults" to "Adults (18+)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "from", + "members.ageGroup" to "Age group", + "members.ageRange" to "Age from - to", + "members.ageToPlaceholder" to "to", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "Birthdate", + "members.bulkActions" to "Bulk actions", + "members.camera" to "Camera", + "members.change" to "Change", + "members.city" to "City", + "members.clearFields" to "Clear fields", + "members.clearFilters" to "Reset filters", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "Create", + "members.createNewMember" to "Create new member", + "members.dataIssueAddress" to "Address missing", + "members.dataIssueBirthdate" to "Birthdate missing", + "members.dataIssueCity" to "City missing", + "members.dataIssueEmail" to "Email missing", + "members.dataIssueGender" to "Gender unclear", + "members.dataIssuePhone" to "Phone missing", + "members.dataIssuePostalCode" to "Postal code missing", + "members.dataIssueStreet" to "Street missing", + "members.dataIssueTrainingGroup" to "Training group missing", + "members.dataQuality" to "Data quality", + "members.dataQualityComplete" to "Data complete", + "members.deactivateMember" to "Deactivate member", + "members.deactivateMemberConfirm" to "Do you really want to deactivate \"{name}\"?", + "members.deactivateMemberTitle" to "Deactivate member", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "Edit member", + "members.editorAssignTrainingGroupHint" to "Please assign at least one training group.", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "Recommended entry", + "members.emailAddress" to "Email address", + "members.emailAddressShort" to "Email address", + "members.emails" to "Email addresses", + "members.errorAddingToGroup" to "Error adding to group", + "members.errorDeactivatingMember" to "Error deactivating member", + "members.errorDeletingImage" to "Image could not be deleted", + "members.errorLoadingImage" to "Error loading image", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "Error loading training participations", + "members.errorMarkingForm" to "Error marking the form.", + "members.errorRemovingFromGroup" to "Error removing from group", + "members.errorRemovingTestMembership" to "Error removing the trial membership.", + "members.errorRotatingImage" to "Error rotating image", + "members.errorSavingMember" to "Error saving member", + "members.errorSettingPrimaryImage" to "Primary image could not be set", + "members.errorTransfer" to "Transfer error", + "members.errorUpdatingRatings" to "Error updating TTR/QTTR values", + "members.errorUploadingImage" to "Image could not be uploaded", + "members.exercises" to "Exercises", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "First name", + "members.formHandedOver" to "Membership form handed over", + "members.formMarkedAsHandedOver" to "Membership form marked as handed over.", + "members.gender" to "Gender", + "members.genderDiverse" to "Diverse", + "members.genderFemale" to "Female", + "members.genderMale" to "Male", + "members.genderUnknown" to "Unknown", + "members.generatePhoneList" to "Generate phone list", + "members.groupPhotoCrop" to "Process group photo", + "members.groupPhotoCropSaved" to "Member photo saved from group photo.", + "members.image" to "Image", + "members.imageDeleted" to "Image deleted.", + "members.imageInternet" to "Image (web?)", + "members.imagePreview" to "Member image preview", + "members.imageUpdated" to "Image updated", + "members.inactive" to "inactive", + "members.inactiveMembers" to "Inactive members", + "members.j11" to "U11", + "members.j13" to "U13", + "members.j15" to "U15", + "members.j17" to "U17 (17 and younger)", + "members.j19" to "U19", + "members.j9" to "U9", + "members.lastName" to "Last name", + "members.lastTrainingFilter" to "Last training", + "members.lastTrainingFilterHasDate" to "With a recorded date", + "members.lastTrainingFilterHint" to "Age-class column: current and next season. Extra detail (last training, participations) on row hover.", + "members.lastTrainingFilterNoDate" to "No last training", + "members.lastTrainingFilterNotInTraining" to "Flagged “no longer training”", + "members.loadingMembers" to "Loading members...", + "members.m11" to "G11", + "members.m13" to "G13", + "members.m15" to "G15", + "members.m19" to "G19", + "members.m9" to "G9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "Member deactivated", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "Membership form handed over", + "members.memberImage" to "Member image", + "members.memberImages" to "Member images", + "members.memberInfo" to "Member info", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "No myTischtennis ID stored", + "members.name" to "Last name, first name", + "members.newMember" to "New member", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "No groups assigned", + "members.noGroupsAvailable" to "No groups available", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "Notes", + "members.noTestMembership" to "No longer a trial member", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "Only active members are included", + "members.openTasks" to "Open tasks", + "members.parent" to "Parent", + "members.parentFallback" to "Parent", + "members.parentName" to "Name (e.g. mother, father)", + "members.phoneList" to "phone-list.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "Phone number", + "members.phoneNumberShort" to "Phone no.", + "members.phones" to "Phone numbers", + "members.picsInInternetAllowed" to "Pictures allowed online", + "members.postalCode" to "ZIP code", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "Primary", + "members.primaryImageUpdated" to "Primary image updated.", + "members.remove" to "Remove", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "Active + data incomplete", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "Data incomplete", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "Search and filter", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "Select file", + "members.showInactiveMembers" to "Show inactive members", + "members.showTrainingParticipationsColumn" to "Show “Training participations” column", + "members.showTtrHistory" to "Show TTR history", + "members.sixOrMoreParticipations" to "6 or more training participations", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "QTTR value", + "members.status" to "Status", + "members.street" to "Street", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "Open", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "Assign training group", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "Review data quality", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "Trial", + "members.testMembers" to "Trial members", + "members.testMembership" to "Trial membership", + "members.testMembershipRemoved" to "Trial membership removed.", + "members.threeOrMoreParticipations" to "3 or more training participations", + "members.title" to "Members", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "Training groups", + "members.trainingParticipations" to "Training participations", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "Transfer members", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "Adults (not youth by cutoff)", + "members.ttAgeClassCol" to "Age class (TT)", + "members.ttFilterGroupJ" to "Boys & girls (mixed)", + "members.ttFilterGroupM" to "Girls only", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "current", + "members.ttSeasonFilter" to "Season (cutoff)", + "members.ttSeasonNextTag" to "upcoming", + "members.ttStichtagHint" to "Cutoff 1 Jan (DTTB). Boys: J classes only. Girls: J and M.", + "members.updateRatings" to "Update TTR/QTTR from myTischtennis", + "members.updating" to "Updating...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "Wants to play", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Cancel", + "messages.confirm" to "Confirm", + "messages.error" to "Error", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Success", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Warning", + "mobile.active" to "Active", + "mobile.add" to "Add", + "mobile.appLoading" to "Loading app", + "mobile.cancel" to "Cancel", + "mobile.clubRequest" to "Request access", + "mobile.clubRequested" to "Requested", + "mobile.createDiaryEntry" to "Create entry", + "mobile.deleteNote" to "Delete note", + "mobile.deleteTag" to "Remove tag", + "mobile.editTimes" to "Edit times", + "mobile.emailAndPasswordRequired" to "Email and password are required", + "mobile.entry" to "Entry", + "mobile.inactive" to "Inactive", + "mobile.language" to "Language", + "mobile.lastTraining" to "Last training", + "mobile.loginFailed" to "Login failed", + "mobile.loginInProgress" to "Signing in...", + "mobile.more" to "More", + "mobile.new" to "New", + "mobile.newNote" to "New note", + "mobile.newTag" to "New tag", + "mobile.noDiaryEntries" to "No diary entries yet", + "mobile.noMembers" to "No members found", + "mobile.noResults" to "No results", + "mobile.noTimes" to "No times", + "mobile.participationTop" to "Top attendance", + "mobile.refresh" to "Refresh", + "mobile.requestedAccess" to "Requested", + "mobile.role" to "Role", + "mobile.saveEntry" to "Save", + "mobile.search" to "Search", + "mobile.select" to "Select", + "mobile.sessionCheck" to "Check session", + "mobile.sessionCheckFailed" to "Session check failed", + "mobile.sessionInvalid" to "Session invalid", + "mobile.sessionValid" to "Session valid", + "mobile.status" to "Status", + "mobile.user" to "User", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Approvals", + "navigation.backToHome" to "Back to home", + "navigation.billing" to "Billing", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Club Settings", + "navigation.clubTournaments" to "Club Tournaments", + "navigation.competitions" to "Competitions", + "navigation.dailyBusiness" to "Daily Business", + "navigation.diary" to "Diary", + "navigation.home" to "Home", + "navigation.login" to "Log In", + "navigation.logout" to "Log Out", + "navigation.logs" to "System Logs", + "navigation.members" to "Members", + "navigation.memberTransfer" to "Member Transfer", + "navigation.myTischtennisAccount" to "myTischtennis Account", + "navigation.orders" to "Orders", + "navigation.permissions" to "Permissions", + "navigation.personalSettings" to "Personal Settings", + "navigation.predefinedActivities" to "Predefined Activities", + "navigation.register" to "Sign Up", + "navigation.schedule" to "Schedules", + "navigation.settings" to "Settings", + "navigation.statistics" to "Training Statistics", + "navigation.teamManagement" to "Team Management", + "navigation.tournamentParticipations" to "Tournament Participations", + "navigation.tournaments" to "Tournaments", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Create order", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Cost", + "orders.dateAutoHint" to "The date is set automatically and every change is logged with a date.", + "orders.errorLoading" to "Orders could not be loaded.", + "orders.errorSaving" to "Order could not be saved.", + "orders.filterAllClubs" to "All clubs", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "All statuses", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "All orders across clubs can be viewed and managed here.", + "orders.globalTitle" to "Orders Across All Clubs", + "orders.history" to "History", + "orders.item" to "Item", + "orders.itemPlaceholder" to "e.g. jersey, hoodie, or paddle case", + "orders.loading" to "Loading orders...", + "orders.member" to "Member", + "orders.memberTitle" to "Orders: {name}", + "orders.noOrdersGlobal" to "There are currently no orders.", + "orders.noOrdersMember" to "There are no orders for this member yet.", + "orders.open" to "Outstanding", + "orders.orderDate" to "Created on", + "orders.paid" to "Paid", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Search by club, member, or item", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "item arrived", + "orders.statusDate" to "Last update", + "orders.statusHandedOver" to "item handed over", + "orders.statusOrdered" to "ordered", + "orders.statusRequested" to "requested", + "orders.title" to "Orders", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Generated:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "League:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Registration for:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Planned league:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Season:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Team age group:", + "pdfGenerator.teamGenderLabel" to "Team gender:", + "pdfGenerator.teamLineupTitle" to "Team line-up", + "pdfGenerator.teamNameLabel" to "Team:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Language", + "settings.languageChanged" to "Language successfully changed", + "settings.languageDescription" to "Choose your preferred language for the application", + "settings.personalSettings" to "Personal Settings", + "settings.selectLanguage" to "Select Language", + "settings.title" to "Settings", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Available players", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Eligibility", + "teamManagement.eligibilityAdultRelease" to "Approved for adults", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Approved + adult reserve", + "teamManagement.eligibilityAdultReserve" to "Adult reserve", + "teamManagement.eligibilityRegular" to "Regular", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Export line-up as PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "First half", + "teamManagement.firstHalfFull" to "First half (July - December)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Please select a league class and save.", + "teamManagement.lineupEmpty" to "No players have been assigned to this team yet.", + "teamManagement.lineupPdfEmpty" to "No players in the line-up – cannot create PDF.", + "teamManagement.lineupPdfFilePrefix" to "Lineup", + "teamManagement.lineupProposal" to "Line-up by QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Team line-up could not be saved.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} is more than 30 QTTR points ahead of {lower}. Please correct this order.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Missing:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Paste and parse the MyTischtennis URL to link the team ID and league data.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Planned league", + "teamManagement.plannedLeagueHint" to "Optional. Independent of the registered league (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "e.g. district league, after next registration …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Second half", + "teamManagement.secondHalfFull" to "Second half (from 1 January)", + "teamManagement.selectedLineup" to "Selected players", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Add pool rule", + "tournaments.address" to "Address", + "tournaments.adjustTournamentSearch" to "Adjust the search term or create a new tournament.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "All participant data is complete.", + "tournaments.allDataCompleteTop3" to "All data for the top 3 placed players is complete.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Please add at least one pool rule for {label} (e.g. places 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Correct", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Create final round from intermediate round", + "tournaments.createFinalFromPreliminary" to "Create final round from preliminary round", + "tournaments.createGroupMatches" to "Create group matches", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Create intermediate round from preliminary round", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Not yet recorded", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Do you really want to delete class \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "All participants will be detached from this class.", + "tournaments.deleteClassTitle" to "Delete class", + "tournaments.deleteExistingPairingsConfirm" to "Existing pairings will be deleted. Continue?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Distribute free tables", + "tournaments.distributeTablesResult" to "Table distribution", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doubles tournament", + "tournaments.doublesTournamentHint" to "Switches all existing classes to doubles and creates new classes as doubles by default.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Suitable for: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Please enter a class name.", + "tournaments.enterExternalParticipantName" to "Please enter at least first name and last name.", + "tournaments.enterMiniLocation" to "Please enter a location.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Error distributing tables.", + "tournaments.errorGeneratingPdf" to "Error generating the PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Final round", + "tournaments.finishMatch" to "Finish", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Generating PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Groups", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Intermediate round (round 2)", + "tournaments.internalStatsAbsoluteRank" to "Total score ranking", + "tournaments.internalStatsAgeFilter" to "Age group & gender (singles)", + "tournaments.internalStatsAgeFilterAll" to "All age classes", + "tournaments.internalStatsAgeFilterNone" to "No age class selected", + "tournaments.internalStatsAgeNoClass" to "No class assigned", + "tournaments.internalStatsAgeSelectAll" to "All", + "tournaments.internalStatsAgeSelectNone" to "None", + "tournaments.internalStatsAverageRank" to "Average per tournament", + "tournaments.internalStatsAvgPoints" to "Avg.", + "tournaments.internalStatsEmpty" to "No data for the selected period.", + "tournaments.internalStatsExportPdf" to "Export as PDF", + "tournaments.internalStatsFilterAgeBands" to "Age classes", + "tournaments.internalStatsFilterGenderColumn" to "Gender", + "tournaments.internalStatsGenderScopeAll" to "All", + "tournaments.internalStatsGenderScopeGirls" to "Girls only", + "tournaments.internalStatsLast12Months" to "Last 12 months", + "tournaments.internalStatsLast3Months" to "Last 3 months", + "tournaments.internalStatsLast6Months" to "Last 6 months", + "tournaments.internalStatsOpenButton" to "Tournament statistics (singles)", + "tournaments.internalStatsPeriod" to "Period", + "tournaments.internalStatsPoints" to "Total", + "tournaments.internalStatsPointsExplain" to "Scoring: In each group, placement is expressed as a percentage (with N ranked players: 1st = 100%, last = 0%, linear in between; tied ranks share the same value). N counts everyone ranked in that group (including guests). With only one player: 100%. Players who reach the knockout get the highest group score in that class plus 1, then one extra point per knockout match won. Club members in singles classes only.", + "tournaments.internalStatsTitle" to "Internal tournaments statistics (singles)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} tournament(s) in this period (excluding mini championships).", + "tournaments.internalStatsTtAdult" to "Adults", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Mark as live", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "At least 2 participants are required for pairings.", + "tournaments.missingDataPDF" to "Missing data as PDF", + "tournaments.missingDataPDFSubtitle" to "Please collect the missing data (marked with ____) from participants and note them here.", + "tournaments.missingDataPDFSubtitleTop3" to "Please collect missing data of the top 3 (marked with ____) and note them here.", + "tournaments.missingDataPDFTitle" to "Missing participant data – Mini championship", + "tournaments.missingDataPDFTitleTop3" to "Missing data – Top 3 Mini championship", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "New set, e.g. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "No matches available where both players are free.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "No suitable class found.", + "tournaments.noFreeTables" to "No free tables available.", + "tournaments.noMatchingTournamentsTitle" to "No matching tournaments", + "tournaments.noOrphanedMatchesFound" to "No orphaned matches found.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "No rules yet. Example: places 1 and 2 -> top round-2 groups.", + "tournaments.noPoolRulesYetFinal" to "No rules yet. Example: places 1 and 2 -> final round.", + "tournaments.noTop3Yet" to "No top 3 placements have been determined yet.", + "tournaments.noTournamentDate" to "No tournament date available.", + "tournaments.noTournamentsCreatedYet" to "No tournaments have been created yet.", + "tournaments.noTrainingOnDate" to "No training session found for {date}.", + "tournaments.noTrainingParticipants" to "No participants were found in the training session for this date.", + "tournaments.noValidTrainingParticipants" to "No valid participants were found in the training session for this date.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Number of tables", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} orphaned matches removed.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Page", + "tournaments.pairingAlreadyExists" to "This pairing already exists.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} pairings created, {errorCount} errors.", + "tournaments.participantConflicts" to "Conflicts", + "tournaments.participantNotFound" to "Participant not found.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} participants are not assigned to a class yet. These should be assigned first.", + "tournaments.phone" to "Phone", + "tournaments.placementsPendingFinals" to "Final placings will appear once multiple groups or a final round exist.", + "tournaments.placementsPendingGroups" to "Group placings will appear once group matches exist.", + "tournaments.placementsPendingNoGroups" to "Placings will appear here once group matches or a final round exist.", + "tournaments.placementsPendingSelectedClass" to "There are no placings for the currently selected class yet.", + "tournaments.placementsPendingTitle" to "Placings not available yet", + "tournaments.placesFromEachGroup" to "Places from each group (e.g. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Player 1", + "tournaments.playerTwo" to "Player 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Play third place match", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Advance: preliminary round → intermediate round (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Advance: round {from} → round {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Ranking", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Number of groups", + "tournaments.roundMode" to "Mode", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Save rounds", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Active tournament", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Please select a tournament first.", + "tournaments.selectTwoDifferentPlayers" to "Please select two different players.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} of {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Invalid server response.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "The intermediate round is optional. If you activate it, a final round follows afterwards. A knockout final round is created as a single bracket.", + "tournaments.stageConfigLoadError" to "Error loading round configuration.", + "tournaments.stageConfigLoading" to "Loading round configuration…", + "tournaments.stageConfigMissingIds" to "Cannot save: club or tournament ID is missing.", + "tournaments.stageConfigTitle" to "Intermediate & Final Round", + "tournaments.stageCreated" to "Round {round} was created.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Assign", + "tournaments.statusActionCreate" to "Create", + "tournaments.statusActionGenerate" to "Generate", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Review", + "tournaments.statusActionStart" to "Start", + "tournaments.statusConfigIncomplete" to "Configuration incomplete", + "tournaments.statusConfigReady" to "Configuration ready", + "tournaments.statusFinished" to "Finished", + "tournaments.statusGroupsMissing" to "Groups not created yet", + "tournaments.statusGroupsReady" to "Groups ready", + "tournaments.statusGroupsRunning" to "Group stage in progress", + "tournaments.statusKnockoutReady" to "Knockout ready to start", + "tournaments.statusKnockoutRunning" to "Knockout in progress", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Open", + "tournaments.statusParticipantsConflicts" to "{count} participants with conflicts", + "tournaments.statusParticipantsReady" to "{count} participants conflict-free", + "tournaments.statusParticipantsUnassigned" to "{count} without class", + "tournaments.statusTournamentCompleted" to "Tournament completed", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Table", + "tournaments.tablesDistributed" to "Tables have been distributed.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Target", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Target number of groups", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Female", + "tournaments.tournamentClassGenderOpen" to "Open (all)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Remove live marker", + "tournaments.unpairedDoubles" to "Doubles without partner", + "tournaments.useIntermediateStage" to "Use intermediate round", + "tournaments.warningBirthDateMissing" to "Birth date is missing for this class", + "tournaments.warningClassMissing" to "Class not found", + "tournaments.warningGenderMismatch" to "Gender does not match class", + "tournaments.warningGenderMissing" to "Gender is missing for this class", + "tournaments.warningMissingPairing" to "Doubles partner missing", + "tournaments.warningTooOldForClass" to "Too old for this class", + "tournaments.warningTooYoungForClass" to "Too young for this class", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Actions", + "trainingStats.activeMembers" to "Active members", + "trainingStats.allTrainingDays" to "All training days", + "trainingStats.attendingMembers" to "Attending members", + "trainingStats.averageParticipationCurrentMonth" to "Average participation (current month)", + "trainingStats.averageParticipationHalfYear" to "Average participation (half-year)", + "trainingStats.averageParticipationLastMonth" to "Average participation (last month)", + "trainingStats.averageParticipationQuarter" to "Average participation (quarter)", + "trainingStats.averageParticipationYear" to "Average participation (year)", + "trainingStats.birthdate" to "Birthdate", + "trainingStats.date" to "Date", + "trainingStats.lastTraining" to "Last training", + "trainingStats.memberParticipations" to "Member participations", + "trainingStats.name" to "Name", + "trainingStats.noParticipants" to "No participants", + "trainingStats.participants" to "Participants", + "trainingStats.participations12Months" to "Participations (12 months)", + "trainingStats.participations3Months" to "Participations (3 months)", + "trainingStats.participationsTotal" to "Participations (total)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Show details", + "trainingStats.title" to "Training statistics", + "trainingStats.trainingDayFilter" to "Training day", + "trainingStats.trainingDays" to "Training days (last 12 months)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Weekday", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val es: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Diario de entrenamiento", + "app.title" to "Diario de entrenamiento", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Activación fallida. Por favor verifica el enlace.", + "auth.confirmPassword" to "Confirmar contraseña", + "auth.email" to "Correo electrónico", + "auth.forgotPassword" to "¿Olvidaste tu contraseña?", + "auth.forgotPasswordDescription" to "Introduce tu dirección de correo electrónico. Recibirás un enlace para restablecer tu contraseña.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Iniciar sesión", + "auth.loginFailed" to "Inicio de sesión fallido. Por favor verifica tus credenciales.", + "auth.loginSuccess" to "Sesión iniciada correctamente", + "auth.logout" to "Cerrar sesión", + "auth.logoutSuccess" to "Sesión cerrada correctamente", + "auth.newPassword" to "Nueva contraseña", + "auth.noAccount" to "¿No tienes cuenta?", + "auth.password" to "Contraseña", + "auth.passwordResetSuccess" to "Tu contraseña ha sido cambiada exitosamente. Ahora puedes iniciar sesión.", + "auth.passwordsDoNotMatch" to "Las contraseñas no coinciden.", + "auth.passwordTooShort" to "La contraseña debe tener al menos 6 caracteres.", + "auth.register" to "Registrarse", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Recordarme", + "auth.resetEmailSent" to "Si existe una cuenta con este correo, se ha enviado un enlace de restablecimiento. Revisa tu bandeja de entrada (y la carpeta de spam).", + "auth.resetFailed" to "No se pudo cambiar la contraseña. El enlace puede haber expirado.", + "auth.resetPassword" to "Establecer nueva contraseña", + "auth.resetRequestFailed" to "Solicitud fallida. Inténtalo de nuevo.", + "auth.saveNewPassword" to "Guardar contraseña", + "auth.saving" to "Guardando...", + "auth.sending" to "Enviando...", + "auth.sendResetLink" to "Enviar enlace", + "auth.sessionExpired" to "Tu sesión ha expirado. Serás desconectado.", + "auth.toLogin" to "Ir al inicio de sesión", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Acceso al club denegado.", + "club.accessRequested" to "Se ha solicitado el acceso.", + "club.accessRequestFailed" to "No se pudo enviar la solicitud de acceso.", + "club.accessRequestPending" to "Se ha solicitado acceso a este club. Por favor, espera.", + "club.create" to "Crear club", + "club.createTitle" to "Crear club", + "club.diary" to "Diario de entrenamiento", + "club.errorLoadingRequests" to "Error al cargar las solicitudes abiertas", + "club.load" to "Cargar", + "club.members" to "Miembros", + "club.mobileSelectHint" to "Selecciona primero un club para usar la app en el smartphone.", + "club.name" to "Nombre del club", + "club.new" to "Nuevo club", + "club.noAccess" to "Todavía no tienes acceso a este club.", + "club.openAccessRequests" to "Solicitudes de acceso abiertas", + "club.openRequests" to "Solicitudes de acceso abiertas", + "club.requestAccess" to "Solicitar acceso", + "club.select" to "Seleccionar club", + "club.selectPlaceholder" to "Elegir club...", + "club.title" to "Club", + "club.trainingDiary" to "Diario de entrenamiento", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Acciones", + "common.active" to "Activo", + "common.add" to "Añadir", + "common.all" to "Todos", + "common.apply" to "Aplicar", + "common.back" to "Atrás", + "common.cancel" to "Cancelar", + "common.choose" to "Elegir", + "common.clear" to "Borrar", + "common.close" to "Cerrar", + "common.confirm" to "Confirmar", + "common.create" to "Crear", + "common.date" to "Fecha", + "common.days" to "días", + "common.delete" to "Eliminar", + "common.description" to "Descripción", + "common.details" to "Detalles", + "common.disabled" to "Deshabilitado", + "common.download" to "Descargar", + "common.edit" to "Editar", + "common.enabled" to "Habilitado", + "common.filter" to "Filtrar", + "common.hours" to "horas", + "common.in" to "en", + "common.inactive" to "Inactivo", + "common.loading" to "Cargando...", + "common.min" to "min", + "common.minutes" to "minutos", + "common.months" to "meses", + "common.move" to "Mover", + "common.name" to "Nombre", + "common.new" to "Nuevo", + "common.next" to "Siguiente", + "common.no" to "No", + "common.ok" to "OK", + "common.optional" to "Opcional", + "common.period" to "Periodo", + "common.previous" to "Anterior", + "common.refresh" to "Recargar", + "common.remove" to "Quitar", + "common.required" to "Obligatorio", + "common.reset" to "Restablecer", + "common.save" to "Guardar", + "common.saved" to "Guardado", + "common.saving" to "Guardando...", + "common.search" to "Buscar", + "common.select" to "Seleccionar", + "common.status" to "Estado", + "common.submit" to "Enviar", + "common.time" to "Hora", + "common.today" to "Hoy", + "common.type" to "Tipo", + "common.update" to "Actualizar", + "common.view" to "Ver", + "common.weeks" to "semanas", + "common.years" to "años", + "common.yes" to "Sí", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Añadir golpe", + "courtDrawingTool.backhand" to "Revés", + "courtDrawingTool.codeLabel" to "Código", + "courtDrawingTool.completeFirstStroke" to "Completar el primer golpe", + "courtDrawingTool.completeFirstStrokeHint" to "Elige posición inicial, lado, efecto y objetivo. Después aparecerá el gráfico.", + "courtDrawingTool.configureExercise" to "Configurar ejercicio", + "courtDrawingTool.counterSpin" to "Contraefecto", + "courtDrawingTool.forehand" to "Derecha", + "courtDrawingTool.noAdditionalStrokes" to "Todavía no se han añadido golpes siguientes.", + "courtDrawingTool.preview" to "Vista previa", + "courtDrawingTool.previewHint" to "El gráfico aparece en cuanto el primer golpe está completamente configurado.", + "courtDrawingTool.service" to "Saque:", + "courtDrawingTool.serviceTitle" to "Saque", + "courtDrawingTool.sidespin" to "Efecto lateral", + "courtDrawingTool.sideUnderspin" to "Lateral cortado", + "courtDrawingTool.spin" to "Efecto:", + "courtDrawingTool.startLeft" to "izquierda", + "courtDrawingTool.startMiddle" to "centro", + "courtDrawingTool.startRight" to "derecha", + "courtDrawingTool.stepAdditionalStrokes" to "4. Golpes siguientes", + "courtDrawingTool.stepAdditionalStrokesHint" to "Opcionalmente añade más pelotas como secuencia.", + "courtDrawingTool.stepFirstStroke" to "2. Primer golpe", + "courtDrawingTool.stepFirstStrokeHint" to "Define el lado y el efecto de la primera pelota.", + "courtDrawingTool.stepStartPosition" to "1. Posición inicial", + "courtDrawingTool.stepStartPositionHint" to "¿Desde qué posición de saque comienza el ejercicio?", + "courtDrawingTool.stepTarget" to "3. Objetivo", + "courtDrawingTool.stepTargetHint" to "Selecciona la zona objetivo del primer golpe.", + "courtDrawingTool.strokeSide" to "Lado", + "courtDrawingTool.strokeTypeBlock" to "Bloqueo", + "courtDrawingTool.strokeTypeChopDefense" to "Defensa cortada", + "courtDrawingTool.strokeTypeCounter" to "Contragolpe", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Tipo de golpe", + "courtDrawingTool.strokeTypeLobDefense" to "Defensa alta", + "courtDrawingTool.strokeTypePush" to "Corto", + "courtDrawingTool.strokeTypeSmash" to "Remate", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Revés media-larga", + "courtDrawingTool.targetBackhandLong" to "Revés largo", + "courtDrawingTool.targetBackhandShort" to "Revés corto", + "courtDrawingTool.targetForehandHalfLong" to "Derecha media-larga", + "courtDrawingTool.targetForehandLong" to "Derecha larga", + "courtDrawingTool.targetForehandShort" to "Derecha corta", + "courtDrawingTool.targetMiddleHalfLong" to "Centro media-larga", + "courtDrawingTool.targetMiddleLong" to "Centro largo", + "courtDrawingTool.targetMiddleShort" to "Centro corto", + "courtDrawingTool.targetPosition" to "Posición objetivo:", + "courtDrawingTool.targetPositionLabel" to "Posición objetivo", + "courtDrawingTool.title" to "Dibujo de ejercicio de tenis de mesa", + "courtDrawingTool.titleLabel" to "Título", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "a", + "courtDrawingTool.underspin" to "Cortado", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Día de entrenamiento activo", + "diary.activities" to "Actividades", + "diary.activity" to "Actividad", + "diary.activityDrawing" to "Dibujo de actividad", + "diary.activityImage" to "Imagen de actividad", + "diary.activityNotFound" to "Actividad no encontrada. Selecciona una de la lista.", + "diary.activityOrTimeblock" to "Actividad / bloque horario", + "diary.activityPlaceholder" to "Actividad", + "diary.activityRequired" to "Introduce una actividad.", + "diary.addActivity" to "Añadir actividad", + "diary.addGroup" to "Añadir grupo", + "diary.addGroupActivity" to "Añadir actividad de grupo", + "diary.addGroupButton" to "+ Grupo", + "diary.addTimeblock" to "Bloque horario", + "diary.all" to "Todos", + "diary.applySuggestion" to "Aplicar sugerencia", + "diary.assignParticipants" to "Asignar participantes", + "diary.assignParticipantsForGroupActivity" to "Asignar participantes a la actividad de grupo", + "diary.assignShort" to "Asignar", + "diary.bookAccident" to "Registrar accidente", + "diary.confirmDelete" to "Confirmar eliminación", + "diary.confirmDeleteDate" to "¿Deseas eliminar realmente esta fecha?", + "diary.confirmDeleteDateDetails" to "También se eliminarán todos los datos asociados.", + "diary.confirmDeleteGroup" to "¿Deseas eliminar realmente el grupo \"{name}\"?", + "diary.createDate" to "Crear fecha", + "diary.createDrawing" to "Crear dibujo de ejercicio", + "diary.createGroups" to "Crear grupos", + "diary.createNew" to "Crear", + "diary.createNewDate" to "Crear nueva fecha", + "diary.date" to "Fecha", + "diary.dateCannotBeDeleted" to "La fecha no se puede eliminar", + "diary.dateCannotBeDeletedDetails" to "Todavía hay contenido (plan, participantes, actividades, accidentes o notas).", + "diary.dateNoLongerCurrent" to "La fecha seleccionada ya no era actual. Inténtalo de nuevo.", + "diary.delete" to "Eliminar", + "diary.deleteDate" to "Eliminar fecha", + "diary.deleteGroup" to "Eliminar grupo", + "diary.duration" to "Duración", + "diary.durationExampleLong" to "p. ej. 2x7 o 3*5", + "diary.durationExampleShort" to "p. ej. 2x7", + "diary.durationMinutes" to "Duración (min)", + "diary.editActivity" to "Editar actividad", + "diary.editGroupActivity" to "Editar actividad de grupo", + "diary.editTrainingTimes" to "Editar horarios de entrenamiento", + "diary.errorCreatingActivity" to "Error al crear la actividad", + "diary.errorCreatingGroups" to "Error al crear grupos", + "diary.errorDeletingGroup" to "Error al eliminar el grupo", + "diary.errorLoadingPredefinedActivities" to "Error al cargar las actividades predefinidas", + "diary.errorMarkingForm" to "Error al marcar el formulario", + "diary.errorOccurred" to "Se ha producido un error. Inténtalo de nuevo.", + "diary.existingGroups" to "Grupos existentes", + "diary.filterAbsent" to "Ausente", + "diary.filterAll" to "Todos", + "diary.filterExcused" to "Justificado", + "diary.filterPresent" to "Presente", + "diary.filterTest" to "Prueba", + "diary.formHandedOver" to "Formulario de afiliación entregado", + "diary.formMarkedAsHandedOver" to "Formulario de afiliación marcado como entregado", + "diary.freeActivities" to "Actividades libres", + "diary.gallery" to "Galería de miembros", + "diary.galleryCreating" to "Creando galería…", + "diary.group" to "Grupo...", + "diary.groupDeletedSuccessfully" to "El grupo se eliminó correctamente.", + "diary.groupManagement" to "Gestión de grupos", + "diary.groupsCreated" to "Se crearon {count} grupos correctamente.", + "diary.groupsLabel" to "Grupos", + "diary.groupsSection" to "Grupos", + "diary.leader" to "Responsable", + "diary.min" to "min", + "diary.minutes" to "Minutos", + "diary.mustCreateAtLeastTwoGroups" to "La primera vez deben crearse al menos 2 grupos.", + "diary.nextAppointment" to "Próxima cita", + "diary.noActiveTrainingDay" to "No se ha seleccionado ningún día de entrenamiento.", + "diary.noEntries" to "No hay entradas", + "diary.noFreeActivitiesYet" to "Todavía no se han registrado actividades libres.", + "diary.noParticipants" to "No hay participantes disponibles para este día de entrenamiento.", + "diary.numberOfGroups" to "Número de grupos", + "diary.oneGroupAdded" to "Se añadió 1 grupo correctamente.", + "diary.openPlanItems" to "{count} abiertos", + "diary.openPlanItemsLabel" to "Estado del plan", + "diary.overallActivity" to "Actividad global", + "diary.participants" to "Participantes", + "diary.participantStatusCancelled" to "Cancelado", + "diary.participantStatusExcused" to "Justificado", + "diary.participantStatusNone" to "Sin estado", + "diary.planActivitiesCount" to "Actividades planificadas", + "diary.planAddHint" to "Añade nuevos elementos del plan con las acciones superiores.", + "diary.planEmptyState" to "Todavía no hay nada registrado en el plan de entrenamiento.", + "diary.quickAdd" to "+ Añadir rápido", + "diary.searchParticipants" to "Buscar participantes", + "diary.selectGroup" to "Seleccionar grupo...", + "diary.selectGroupAndActivity" to "Selecciona un grupo e introduce una actividad.", + "diary.selectParticipantAndNote" to "Selecciona un participante e introduce una nota.", + "diary.selectTags" to "Seleccionar etiquetas", + "diary.selectTrainingGroup" to "Seleccionar grupo de entrenamiento", + "diary.selectTrainingGroupPlaceholder" to "Selecciona...", + "diary.showImage" to "Mostrar imagen/dibujo", + "diary.skipSuggestion" to "Continuar sin sugerencia", + "diary.standardActivities" to "Actividades estándar", + "diary.standardActivityAddError" to "No se pudo añadir la actividad estándar.", + "diary.standardDurationShort" to "min", + "diary.startTime" to "Hora de inicio", + "diary.statusEmpty" to "Este día de entrenamiento aún está vacío.", + "diary.statusInProgress" to "Este día de entrenamiento está preparado parcialmente.", + "diary.statusOpenShort" to "Abierto", + "diary.statusReady" to "Los horarios y el plan de entrenamiento están definidos.", + "diary.statusReadyShort" to "Listo", + "diary.suggestion" to "Sugerencia", + "diary.timeblock" to "Bloque horario", + "diary.timeblocksCount" to "Bloques horarios", + "diary.title" to "Diario de entrenamiento", + "diary.today" to "Hoy", + "diary.trainingDayAsPDF" to "Descargar día de entrenamiento como PDF", + "diary.trainingDayAsPDFShort" to "Día de entrenamiento en PDF", + "diary.trainingDayChecklist" to "Lista de control final", + "diary.trainingDaySection" to "Día de entrenamiento", + "diary.trainingDaySummaryPdfShort" to "Resumen de participantes en PDF", + "diary.trainingEnd" to "Fin del entrenamiento", + "diary.trainingPlan" to "Plan de entrenamiento", + "diary.trainingPlanAsPDF" to "Plan de entrenamiento en PDF", + "diary.trainingPlanPdfShort" to "Planificación en PDF", + "diary.trainingStart" to "Inicio del entrenamiento", + "diary.trainingTimesUpdated" to "Los horarios de entrenamiento se actualizaron correctamente.", + "diary.trainingWindow" to "Franja de entrenamiento", + "diary.trainingWindowUnset" to "Aún no definido", + "diary.unassignedPlanItems" to "{count} abiertos", + "diary.updateTimes" to "Actualizar horarios", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Alemán)", + "languages.de-CH" to "Schwiizerdütsch (Alemán suizo)", + "languages.en-AU" to "English (Inglés (AU))", + "languages.en-GB" to "English (Inglés (GB))", + "languages.en-US" to "English (Inglés (US))", + "languages.es" to "Español", + "languages.fil" to "Filipino", + "languages.fr" to "Français (Francés)", + "languages.it" to "Italiano", + "languages.ja" to "日本語 (Japonés)", + "languages.pl" to "Polski (Polaco)", + "languages.th" to "ไทย (Tailandés)", + "languages.tl" to "Tagalog (Tagalo)", + "languages.zh" to "中文 (Chino)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Acciones", + "members.active" to "Activo", + "members.activeMembers" to "Miembros activos", + "members.addEmail" to "Añadir dirección de correo", + "members.addGroup" to "Añadir grupo...", + "members.addPhone" to "Añadir número de teléfono", + "members.address" to "Dirección", + "members.adultReleaseApproved" to "Aprobado para adultos", + "members.adultReserveApproved" to "Reserva adulta", + "members.adults" to "Adultos (20+)", + "members.age" to "Edad", + "members.ageFromPlaceholder" to "de", + "members.ageGroup" to "Categoría de edad", + "members.ageRange" to "Edad de - a", + "members.ageToPlaceholder" to "a", + "members.batchFormsMarkedEmpty" to "No hay formularios sin verificar en la selección actual.", + "members.batchFormsMarkedSuccess" to "Se marcaron {count} formulario(s) como verificados.", + "members.batchMarkedRegularEmpty" to "No hay miembros de prueba en la selección actual.", + "members.batchMarkedRegularSuccess" to "Se marcaron {count} miembro(s) de prueba como regulares.", + "members.batchPartialFailure" to "{success} correctos, {failed} fallidos.", + "members.birthdate" to "Fecha de nacimiento", + "members.bulkActions" to "Acciones en lote", + "members.camera" to "Cámara", + "members.change" to "Cambiar", + "members.city" to "Ciudad", + "members.clearFields" to "Vaciar campos", + "members.clearFilters" to "Restablecer filtros", + "members.clickTtRequestAction" to "Solicitar autorización Click-TT", + "members.clickTtRequestConfirm" to "¿Deseas iniciar la solicitud Click-TT automatizada para {name}?", + "members.clickTtRequestError" to "No se pudo enviar la solicitud Click-TT.", + "members.clickTtRequestFailedLog" to "Falló la solicitud Click-TT", + "members.clickTtRequestHint" to "La solicitud se procesa automáticamente mediante el flujo Click-TT del backend.", + "members.clickTtRequestPending" to "Solicitud Click-TT en curso", + "members.clickTtRequestPendingShort" to "En curso...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Inicia sesión en click-tt y envía allí la solicitud.", + "members.clickTtRequestTitle" to "Iniciar solicitud Click-TT", + "members.clickTtSubmitted" to "Click-TT enviado", + "members.closeEditor" to "Cerrar editor", + "members.composeEmail" to "Preparar correo", + "members.contact" to "Contacto", + "members.copyContactSummary" to "Copiar resumen de contactos", + "members.copyContactSummarySuccess" to "Resumen de contactos copiado al portapapeles.", + "members.copyEmails" to "Copiar correos", + "members.copyEmailsEmpty" to "No hay direcciones de correo en la selección actual.", + "members.copyEmailsSuccess" to "La lista de correo se copió al portapapeles.", + "members.copyPhones" to "Copiar teléfonos", + "members.copyPhonesEmpty" to "No hay números de teléfono en la selección actual.", + "members.copyPhonesSuccess" to "La lista telefónica se copió al portapapeles.", + "members.create" to "Crear", + "members.createNewMember" to "Crear nuevo miembro", + "members.dataIssueAddress" to "Falta la dirección", + "members.dataIssueBirthdate" to "Falta la fecha de nacimiento", + "members.dataIssueCity" to "Falta la ciudad", + "members.dataIssueEmail" to "Falta el correo", + "members.dataIssueGender" to "Sexo no definido", + "members.dataIssuePhone" to "Falta el teléfono", + "members.dataIssuePostalCode" to "Falta el código postal", + "members.dataIssueStreet" to "Falta la calle", + "members.dataIssueTrainingGroup" to "Falta el grupo de entrenamiento", + "members.dataQuality" to "Calidad de los datos", + "members.dataQualityComplete" to "Datos completos", + "members.deactivateMember" to "Desactivar miembro", + "members.deactivateMemberConfirm" to "¿Realmente deseas desactivar a \"{name}\"?", + "members.deactivateMemberTitle" to "Desactivar miembro", + "members.deleteImageConfirm" to "¿Deseas eliminar realmente esta imagen?", + "members.deleteImageTitle" to "Eliminar imagen", + "members.editHint" to "Un clic en una fila abre el editor.", + "members.editMember" to "Editar miembro", + "members.editorAssignTrainingGroupHint" to "Asigna al menos un grupo de entrenamiento.", + "members.editorCreateHint" to "Crear un nuevo miembro e introducir directamente sus datos de contacto.", + "members.editorEditHint" to "Editar los datos de {name}.", + "members.editorRecommendedEntry" to "Entrada recomendada", + "members.emailAddress" to "Dirección de correo", + "members.emailAddressShort" to "Correo", + "members.emails" to "Direcciones de correo", + "members.errorAddingToGroup" to "Error al añadir al grupo", + "members.errorDeactivatingMember" to "Error al desactivar al miembro", + "members.errorDeletingImage" to "No se pudo eliminar la imagen", + "members.errorLoadingImage" to "Error al cargar la imagen", + "members.errorLoadingMembers" to "No se pudo cargar la lista de miembros.", + "members.errorLoadingTrainingParticipations" to "Error al cargar las participaciones de entrenamiento", + "members.errorMarkingForm" to "Error al marcar el formulario.", + "members.errorRemovingFromGroup" to "Error al quitar del grupo", + "members.errorRemovingTestMembership" to "Error al eliminar la membresía de prueba.", + "members.errorRotatingImage" to "Error al girar la imagen", + "members.errorSavingMember" to "Error al guardar el miembro", + "members.errorSettingPrimaryImage" to "No se pudo establecer la imagen principal", + "members.errorTransfer" to "Error en la transferencia", + "members.errorUpdatingRatings" to "Error al actualizar los valores TTR/QTTR", + "members.errorUploadingImage" to "No se pudo subir la imagen", + "members.exercises" to "Ejercicios", + "members.exportCsv" to "Exportar CSV", + "members.exportCsvFile" to "seleccion-miembros.csv", + "members.exportEmails" to "Correo", + "members.exportMembersSelected" to "miembros seleccionados actualmente", + "members.exportPhones" to "Teléfono", + "members.exportPreview" to "Vista previa de exportación", + "members.exportPreviewEmpty" to "No hay miembros en la selección actual", + "members.exportPreviewNames" to "Vista previa", + "members.exportReachableByEmail" to "con dirección de correo", + "members.exportReachableByPhone" to "con número de teléfono", + "members.firstName" to "Nombre", + "members.formHandedOver" to "Formulario de afiliación entregado", + "members.formMarkedAsHandedOver" to "El formulario de afiliación se marcó como entregado.", + "members.gender" to "Sexo", + "members.genderDiverse" to "Diverso", + "members.genderFemale" to "Femenino", + "members.genderMale" to "Masculino", + "members.genderUnknown" to "Desconocido", + "members.generatePhoneList" to "Generar lista telefónica", + "members.groupPhotoCrop" to "Procesar foto de grupo", + "members.groupPhotoCropSaved" to "Foto del miembro guardada desde la foto de grupo.", + "members.image" to "Imagen", + "members.imageDeleted" to "La imagen ha sido eliminada.", + "members.imageInternet" to "Imagen (web?)", + "members.imagePreview" to "Vista previa de la imagen del miembro", + "members.imageUpdated" to "Imagen actualizada", + "members.inactive" to "inactivo", + "members.inactiveMembers" to "Miembros inactivos", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "U17 (17 años o menos)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Apellido", + "members.lastTrainingFilter" to "Último entrenamiento", + "members.lastTrainingFilterHasDate" to "Con fecha registrada", + "members.lastTrainingFilterHint" to "Tabla: temporada actual en la columna de edad. Detalle completo (ambas temporadas, último entrenamiento, participaciones) al pasar el ratón por la fila.", + "members.lastTrainingFilterNoDate" to "Sin último entrenamiento", + "members.lastTrainingFilterNotInTraining" to "« Ya no entrena »", + "members.loadingMembers" to "Cargando miembros...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Marcar formulario como verificado", + "members.markFormsForSelection" to "Verificar formularios de la selección", + "members.markRegular" to "Marcar como regular", + "members.markRegularForSelection" to "Marcar selección como regular", + "members.memberDeactivated" to "Miembro desactivado", + "members.memberDetails" to "Detalles del miembro", + "members.memberFormHandedOver" to "Formulario de afiliación entregado", + "members.memberImage" to "Imagen del miembro", + "members.memberImages" to "Imágenes del miembro", + "members.memberInfo" to "Información del miembro", + "members.memberScope" to "Ámbito de miembros", + "members.missingTtrHistoryId" to "No hay ID de myTischtennis almacenado", + "members.name" to "Apellido, nombre", + "members.newMember" to "Nuevo miembro", + "members.noAddressShort" to "Sin dirección", + "members.noEmailShort" to "Sin correo", + "members.noGroupsAssigned" to "No hay grupos asignados", + "members.noGroupsAvailable" to "No hay grupos disponibles", + "members.noOpenTasks" to "No hay tareas abiertas", + "members.noPhoneShort" to "Sin teléfono", + "members.notes" to "Notas", + "members.noTestMembership" to "Ya no es miembro de prueba", + "members.notInTrainingTooltip" to "Sin participación desde hace {weeks} semanas de entrenamiento", + "members.onlyActiveMembers" to "Solo se incluyen miembros activos", + "members.openTasks" to "Tareas abiertas", + "members.parent" to "Tutor", + "members.parentFallback" to "Tutor", + "members.parentName" to "Nombre (p. ej. madre, padre)", + "members.phoneList" to "lista-telefonica.pdf", + "members.phoneListForSelection" to "Lista telefónica de la selección", + "members.phoneListSelectionFile" to "lista-telefonica-seleccion.pdf", + "members.phoneNumber" to "Número de teléfono", + "members.phoneNumberShort" to "Tel.", + "members.phones" to "Números de teléfono", + "members.picsInInternetAllowed" to "Fotos permitidas en internet", + "members.postalCode" to "Código postal", + "members.previewLastTraining" to "Último entrenamiento", + "members.previewNoLastTraining" to "Aún sin participación", + "members.primary" to "Principal", + "members.primaryImageUpdated" to "Imagen principal actualizada.", + "members.remove" to "Quitar", + "members.resultsVisible" to "miembros visibles", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Activos", + "members.scopeActiveDataIncomplete" to "Activo + datos incompletos", + "members.scopeActiveTest" to "Activo + prueba", + "members.scopeAll" to "Todos", + "members.scopeDataIncomplete" to "Datos incompletos", + "members.scopeInactive" to "Inactivos", + "members.scopeNeedsForm" to "Formulario sin verificar", + "members.scopeNotTraining" to "Ya no entrena", + "members.scopeTest" to "Prueba", + "members.search" to "Buscar", + "members.searchAndFilter" to "Búsqueda y filtros", + "members.searchPlaceholder" to "Buscar por nombre, ciudad, teléfono o correo", + "members.selectFile" to "Seleccionar archivo", + "members.showInactiveMembers" to "Mostrar miembros inactivos", + "members.showTrainingParticipationsColumn" to "Mostrar columna « Participaciones »", + "members.showTtrHistory" to "Mostrar historial TTR", + "members.sixOrMoreParticipations" to "6 o más participaciones en entrenamiento", + "members.sortAge" to "Edad", + "members.sortBirthday" to "Fecha de nacimiento", + "members.sortBy" to "Ordenar por", + "members.sortFirstName" to "Nombre", + "members.sortLastName" to "Apellido", + "members.sortLastTraining" to "Último entrenamiento", + "members.sortOpenTasks" to "Tareas abiertas", + "members.sortQttr" to "Valor QTTR", + "members.status" to "Estado", + "members.street" to "Calle", + "members.subtitle" to "Buscar, filtrar y editar miembros directamente.", + "members.taskActionMarkRegular" to "Marcar regular", + "members.taskActionRequest" to "Solicitar", + "members.taskActionReview" to "Abrir", + "members.taskActionVerify" to "Verificar", + "members.taskAssignTrainingGroup" to "Asignar grupo de entrenamiento", + "members.taskCheckClickTt" to "Revisar autorización Click-TT", + "members.taskCheckDataQuality" to "Revisar calidad de datos", + "members.taskCheckTrainingStatus" to "Revisar estado de entrenamiento", + "members.taskReviewTrialStatus" to "Revisar estado de prueba", + "members.taskVerifyForm" to "Verificar formulario", + "members.testMember" to "Prueba", + "members.testMembers" to "Miembros de prueba", + "members.testMembership" to "Membresía de prueba", + "members.testMembershipRemoved" to "Se eliminó la membresía de prueba.", + "members.threeOrMoreParticipations" to "3 o más participaciones en entrenamiento", + "members.title" to "Miembros", + "members.toggleSortDirection" to "Cambiar dirección de ordenación", + "members.trainingGroups" to "Grupos de entrenamiento", + "members.trainingParticipations" to "Participaciones en entrenamiento", + "members.transferErrorTitle" to "Error de transferencia", + "members.transferMembers" to "Transferir miembros", + "members.transferSuccessTitle" to "Transferencia correcta", + "members.ttAdult" to "Adultos (no juvenil según corte)", + "members.ttAgeClassCol" to "Cat. edad (TT)", + "members.ttFilterGroupJ" to "Niños y niñas (mixto)", + "members.ttFilterGroupM" to "Solo niñas", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "actual", + "members.ttSeasonFilter" to "Temporada (corte)", + "members.ttSeasonNextTag" to "próxima", + "members.ttStichtagHint" to "Corte el 1 de enero (DTTB). Niños: solo clases J. Niñas: J y M.", + "members.updateRatings" to "Actualizar TTR/QTTR desde myTischtennis", + "members.updating" to "Actualizando...", + "members.visibleMembers" to "Miembros visibles", + "members.wantsToPlay" to "Quiere jugar", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Cancelar", + "messages.confirm" to "Confirmar", + "messages.error" to "Error", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Información", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Éxito", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Advertencia", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Aprobaciones", + "navigation.backToHome" to "Volver al inicio", + "navigation.billing" to "Facturación", + "navigation.clickTtAccount" to "Cuenta HTTV / click-TT", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Configuración del club", + "navigation.clubTournaments" to "Torneos del club", + "navigation.competitions" to "Competiciones", + "navigation.dailyBusiness" to "Gestión diaria", + "navigation.diary" to "Diario", + "navigation.home" to "Inicio", + "navigation.login" to "Iniciar sesión", + "navigation.logout" to "Cerrar sesión", + "navigation.logs" to "Registros del sistema", + "navigation.members" to "Miembros", + "navigation.memberTransfer" to "Transferencia de miembros", + "navigation.myTischtennisAccount" to "Cuenta myTischtennis", + "navigation.orders" to "Pedidos", + "navigation.permissions" to "Permisos", + "navigation.personalSettings" to "Configuración personal", + "navigation.predefinedActivities" to "Actividades predefinidas", + "navigation.register" to "Registrarse", + "navigation.schedule" to "Calendarios", + "navigation.settings" to "Configuración", + "navigation.statistics" to "Estadísticas de entrenamiento", + "navigation.teamManagement" to "Gestión de equipos", + "navigation.tournamentParticipations" to "Participaciones en torneos", + "navigation.tournaments" to "Torneos", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Crear pedido", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Coste", + "orders.dateAutoHint" to "La fecha se establece automáticamente y cada cambio se registra con fecha.", + "orders.errorLoading" to "No se pudieron cargar los pedidos.", + "orders.errorSaving" to "No se pudo guardar el pedido.", + "orders.filterAllClubs" to "Todos los clubes", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Todos los estados", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Aquí se pueden ver y gestionar los pedidos de todos los clubes.", + "orders.globalTitle" to "Pedidos de todos los clubes", + "orders.history" to "Historial", + "orders.item" to "Artículo", + "orders.itemPlaceholder" to "p. ej. camiseta, sudadera o funda de pala", + "orders.loading" to "Cargando pedidos...", + "orders.member" to "Miembro", + "orders.memberTitle" to "Pedidos: {name}", + "orders.noOrdersGlobal" to "Actualmente no hay pedidos.", + "orders.noOrdersMember" to "Todavía no hay pedidos para este miembro.", + "orders.open" to "Pendiente", + "orders.orderDate" to "Creado el", + "orders.paid" to "Pagado", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Buscar por club, miembro o artículo", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Estado", + "orders.statusArrived" to "artículo recibido", + "orders.statusDate" to "Último cambio", + "orders.statusHandedOver" to "artículo entregado", + "orders.statusOrdered" to "pedido", + "orders.statusRequested" to "deseado", + "orders.title" to "Pedidos", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Generado:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Liga:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Inscripción para:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Liga prevista:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Temporada:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Categoría de edad del equipo:", + "pdfGenerator.teamGenderLabel" to "Género del equipo:", + "pdfGenerator.teamLineupTitle" to "Alineación del equipo", + "pdfGenerator.teamNameLabel" to "Equipo:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Idioma", + "settings.languageChanged" to "Idioma cambiado correctamente", + "settings.languageDescription" to "Elige tu idioma preferido para la aplicación", + "settings.personalSettings" to "Configuración personal", + "settings.selectLanguage" to "Seleccionar idioma", + "settings.title" to "Configuración", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Jugadores disponibles", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Elegibilidad", + "teamManagement.eligibilityAdultRelease" to "Autorizado para adultos", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Autorizado + reserva en adultos", + "teamManagement.eligibilityAdultReserve" to "Reserva en adultos", + "teamManagement.eligibilityRegular" to "Regular", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Exportar alineación como PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Primera vuelta", + "teamManagement.firstHalfFull" to "Primera vuelta (julio - diciembre)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Seleccione una liga y guarde.", + "teamManagement.lineupEmpty" to "Todavía no se ha inscrito ningún jugador para este equipo.", + "teamManagement.lineupPdfEmpty" to "No hay jugadores en la alineación – no se puede crear el PDF.", + "teamManagement.lineupPdfFilePrefix" to "Alineacion", + "teamManagement.lineupProposal" to "Alineación por QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "No se pudo guardar la alineación del equipo.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} tiene más de 30 puntos QTTR de ventaja sobre {lower}. Corrige este orden.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Información faltante:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Pegue y analice la URL de MyTischtennis para vincular el ID del equipo y los datos de la liga.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Liga prevista", + "teamManagement.plannedLeagueHint" to "Opcional. Independiente de la liga registrada (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "p. ej. liga regional, tras la próxima inscripción …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Segunda vuelta", + "teamManagement.secondHalfFull" to "Segunda vuelta (desde el 1 de enero)", + "teamManagement.selectedLineup" to "Jugadores inscritos", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Añadir regla de grupos", + "tournaments.address" to "Dirección", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Todos los datos de los participantes están completos.", + "tournaments.allDataCompleteTop3" to "Todos los datos de los 3 primeros clasificados están completos.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Añade al menos una regla de grupos para {label} (p. ej. puestos 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Corregir", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Crear ronda final desde la intermedia", + "tournaments.createFinalFromPreliminary" to "Crear ronda final desde la preliminar", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Crear ronda intermedia desde la preliminar", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Aún no registrado", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "¿De verdad quieres eliminar la clase \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "Todos los participantes se desvincularán de esta clase.", + "tournaments.deleteClassTitle" to "Eliminar clase", + "tournaments.deleteExistingPairingsConfirm" to "Se eliminarán las parejas existentes. ¿Continuar?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Distribuir mesas libres", + "tournaments.distributeTablesResult" to "Distribución de mesas", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Adecuado para: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Introduce un nombre de clase.", + "tournaments.enterExternalParticipantName" to "Introduce al menos el nombre y el apellido.", + "tournaments.enterMiniLocation" to "Introduce una ubicación.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Error al distribuir las mesas.", + "tournaments.errorGeneratingPdf" to "Error al generar el PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Ronda final", + "tournaments.finishMatch" to "Finalizar", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Generando PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Grupos", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Ronda intermedia (ronda 2)", + "tournaments.internalStatsAbsoluteRank" to "Clasificación por suma", + "tournaments.internalStatsAgeFilter" to "Edad y género (individual)", + "tournaments.internalStatsAgeFilterAll" to "Todas las categorías", + "tournaments.internalStatsAgeFilterNone" to "Ninguna categoría seleccionada", + "tournaments.internalStatsAgeNoClass" to "Sin clase asignada", + "tournaments.internalStatsAgeSelectAll" to "Todas", + "tournaments.internalStatsAgeSelectNone" to "Ninguna", + "tournaments.internalStatsAverageRank" to "Clasificación por media (por torneo)", + "tournaments.internalStatsAvgPoints" to "Media", + "tournaments.internalStatsEmpty" to "No hay datos para el periodo seleccionado.", + "tournaments.internalStatsExportPdf" to "Exportar como PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Últimos 12 meses", + "tournaments.internalStatsLast3Months" to "Últimos 3 meses", + "tournaments.internalStatsLast6Months" to "Últimos 6 meses", + "tournaments.internalStatsOpenButton" to "Estadísticas de torneos (individual)", + "tournaments.internalStatsPeriod" to "Periodo", + "tournaments.internalStatsPoints" to "Total", + "tournaments.internalStatsPointsExplain" to "Puntuación: en cada grupo la posición se expresa en porcentaje (con N clasificados: 1.º = 100 %, último = 0 %, lineal entre medias; empates = mismo valor). N cuenta a todos los clasificados del grupo (invitados incluidos). Con un solo jugador: 100 %. En KO: máximo de grupo de la clase + 1, más 1 por partido KO ganado. Solo socios en individual.", + "tournaments.internalStatsTitle" to "Estadísticas de torneos internos (individual)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} torneo(s) en el periodo (sin minicampeonatos).", + "tournaments.internalStatsTtAdult" to "Adultos", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Marcar como en vivo", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Se requieren al menos 2 participantes para formar parejas.", + "tournaments.missingDataPDF" to "Datos faltantes como PDF", + "tournaments.missingDataPDFSubtitle" to "Por favor, recopile los datos faltantes (marcados con ____) de los participantes y anótelos aquí.", + "tournaments.missingDataPDFSubtitleTop3" to "Por favor, recopile los datos faltantes de los 3 primeros (marcados con ____) y anótelos aquí.", + "tournaments.missingDataPDFTitle" to "Datos de participantes faltantes – Mini campeonato", + "tournaments.missingDataPDFTitleTop3" to "Datos faltantes – Top 3 Mini campeonato", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Nuevo set, p. ej. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "No hay partidos disponibles en los que ambos jugadores estén libres.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "No se encontró una categoría adecuada.", + "tournaments.noFreeTables" to "No hay mesas libres disponibles.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "No se encontraron partidos huérfanos.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Aún no hay reglas. Ejemplo: puestos 1 y 2 -> grupos superiores de la ronda 2.", + "tournaments.noPoolRulesYetFinal" to "Aún no hay reglas. Ejemplo: puestos 1 y 2 -> ronda final.", + "tournaments.noTop3Yet" to "Aún no se han determinado los 3 primeros puestos.", + "tournaments.noTournamentDate" to "No hay fecha de torneo disponible.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "No se encontró ninguna sesión de entrenamiento para {date}.", + "tournaments.noTrainingParticipants" to "No se encontraron participantes en la sesión de entrenamiento para esta fecha.", + "tournaments.noValidTrainingParticipants" to "No se encontraron participantes válidos en la sesión de entrenamiento para esta fecha.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Número de mesas", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "Se eliminaron {count} partidos huérfanos.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Página", + "tournaments.pairingAlreadyExists" to "Esta pareja ya existe.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "Se crearon {successCount} parejas, {errorCount} errores.", + "tournaments.participantConflicts" to "Conflictos", + "tournaments.participantNotFound" to "Participante no encontrado.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Teléfono", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Puestos de cada grupo (p. ej. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Jugador 1", + "tournaments.playerTwo" to "Jugador 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Jugar por el tercer puesto", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Avance: ronda preliminar → ronda intermedia (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Avance: ronda {from} → ronda {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Clasificación", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Número de grupos", + "tournaments.roundMode" to "Modo", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Guardar rondas", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Selecciona primero un torneo.", + "tournaments.selectTwoDifferentPlayers" to "Selecciona dos jugadores distintos.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Respuesta del servidor no válida.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "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.", + "tournaments.stageConfigLoadError" to "Error al cargar la configuración de rondas.", + "tournaments.stageConfigLoading" to "Cargando configuración de rondas…", + "tournaments.stageConfigMissingIds" to "No se puede guardar: falta el ID del club o del torneo.", + "tournaments.stageConfigTitle" to "Ronda intermedia y final", + "tournaments.stageCreated" to "Se creó la ronda {round}.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Terminado", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "En juego", + "tournaments.statusOpen" to "Abierto", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Mesa", + "tournaments.tablesDistributed" to "Las mesas han sido distribuidas.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Destino", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Número de grupos destino", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Femenino", + "tournaments.tournamentClassGenderOpen" to "Abierta (todos)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Quitar marca en vivo", + "tournaments.unpairedDoubles" to "Dobles sin pareja", + "tournaments.useIntermediateStage" to "Usar ronda intermedia", + "tournaments.warningBirthDateMissing" to "Falta la fecha de nacimiento para esta categoría", + "tournaments.warningClassMissing" to "Categoría no encontrada", + "tournaments.warningGenderMismatch" to "El género no coincide con la categoría", + "tournaments.warningGenderMissing" to "Falta el género para esta categoría", + "tournaments.warningMissingPairing" to "Falta la pareja de dobles", + "tournaments.warningTooOldForClass" to "Demasiado mayor para esta categoría", + "tournaments.warningTooYoungForClass" to "Demasiado joven para esta categoría", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Acciones", + "trainingStats.activeMembers" to "Miembros activos", + "trainingStats.allTrainingDays" to "Todos los días de entrenamiento", + "trainingStats.attendingMembers" to "Miembros presentes", + "trainingStats.averageParticipationCurrentMonth" to "Participación media (mes actual)", + "trainingStats.averageParticipationHalfYear" to "Participación media (semestre)", + "trainingStats.averageParticipationLastMonth" to "Participación media (mes anterior)", + "trainingStats.averageParticipationQuarter" to "Participación media (trimestre)", + "trainingStats.averageParticipationYear" to "Participación media (año)", + "trainingStats.birthdate" to "Fecha de nacimiento", + "trainingStats.date" to "Fecha", + "trainingStats.lastTraining" to "Último entrenamiento", + "trainingStats.memberParticipations" to "Participaciones de los miembros", + "trainingStats.name" to "Nombre", + "trainingStats.noParticipants" to "Sin participantes", + "trainingStats.participants" to "Participantes", + "trainingStats.participations12Months" to "Participaciones (12 meses)", + "trainingStats.participations3Months" to "Participaciones (3 meses)", + "trainingStats.participationsTotal" to "Participaciones (total)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Mostrar detalles", + "trainingStats.title" to "Estadísticas de entrenamiento", + "trainingStats.trainingDayFilter" to "Día de entrenamiento", + "trainingStats.trainingDays" to "Días de entrenamiento (últimos 12 meses)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Día de la semana", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val fil: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Training Diary", + "app.title" to "Training Diary", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Nabigo ang activation. Suriin ang link.", + "auth.confirmPassword" to "Kumpirmahin ang password", + "auth.email" to "Email", + "auth.forgotPassword" to "Nakalimutan ang password?", + "auth.forgotPasswordDescription" to "Ilagay ang iyong email address. Makakatanggap ka ng link para i-reset ang password.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Mag-login", + "auth.loginFailed" to "Nabigo ang pag-login. Suriin ang iyong credentials.", + "auth.loginSuccess" to "Matagumpay na nag-login", + "auth.logout" to "Mag-logout", + "auth.logoutSuccess" to "Matagumpay na nag-logout", + "auth.newPassword" to "Bagong password", + "auth.noAccount" to "Wala pang account?", + "auth.password" to "Password", + "auth.passwordResetSuccess" to "Matagumpay na nabago ang password. Maaari ka nang mag-login.", + "auth.passwordsDoNotMatch" to "Hindi magkatugma ang mga password.", + "auth.passwordTooShort" to "Ang password ay dapat hindi bababa sa 6 na character.", + "auth.register" to "Magrehistro", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Tandaan ako", + "auth.resetEmailSent" to "Kung may account sa email na ito, ipinadala na ang reset link. Suriin ang inbox (at spam folder).", + "auth.resetFailed" to "Hindi nabago ang password. Maaaring nag-expire na ang link.", + "auth.resetPassword" to "Magtakda ng bagong password", + "auth.resetRequestFailed" to "Nabigo ang request. Subukan ulit.", + "auth.saveNewPassword" to "I-save ang password", + "auth.saving" to "Sine-save...", + "auth.sending" to "Ipinapadala...", + "auth.sendResetLink" to "Ipadala ang link", + "auth.sessionExpired" to "Nag-expire na ang iyong session. Ikaw ay ma-logout.", + "auth.toLogin" to "Pumunta sa login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Hindi pinapayagan ang access sa club na ito.", + "club.accessRequested" to "Naipadala na ang hiling sa access.", + "club.accessRequestFailed" to "Hindi naipadala ang hiling sa access.", + "club.accessRequestPending" to "Humiling ka na ng access sa club na ito. Maghintay lamang.", + "club.create" to "Gumawa ng club", + "club.createTitle" to "Gumawa ng club", + "club.diary" to "Talaarawan ng pagsasanay", + "club.errorLoadingRequests" to "Error sa pag-load ng mga nakabinbing hiling", + "club.load" to "I-load", + "club.members" to "Mga miyembro", + "club.mobileSelectHint" to "Pumili muna ng club para magamit ang app sa smartphone.", + "club.name" to "Pangalan ng club", + "club.new" to "Bagong club", + "club.noAccess" to "Wala ka pang pahintulot na ma-access ang club na ito.", + "club.openAccessRequests" to "Mga nakabinbing hiling sa access", + "club.openRequests" to "Mga nakabinbing hiling sa access", + "club.requestAccess" to "Humiling ng access", + "club.select" to "Pumili ng club", + "club.selectPlaceholder" to "Pumili ng club...", + "club.title" to "Klub", + "club.trainingDiary" to "Talaarawan ng pagsasanay", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Mga aksyon", + "common.active" to "Aktibo", + "common.add" to "Magdagdag", + "common.all" to "Lahat", + "common.apply" to "Ilapat", + "common.back" to "Bumalik", + "common.cancel" to "Kanselahin", + "common.choose" to "Pumili", + "common.clear" to "Burahin", + "common.close" to "Isara", + "common.confirm" to "Kumpirmahin", + "common.create" to "Lumikha", + "common.date" to "Petsa", + "common.days" to "Araw", + "common.delete" to "Tanggalin", + "common.description" to "Paglalarawan", + "common.details" to "Mga detalye", + "common.disabled" to "Hindi pinagana", + "common.download" to "I-download", + "common.edit" to "I-edit", + "common.enabled" to "Pinagana", + "common.filter" to "I-filter", + "common.hours" to "Oras", + "common.in" to "sa", + "common.inactive" to "Hindi aktibo", + "common.loading" to "Naglo-load...", + "common.min" to "Min", + "common.minutes" to "Minuto", + "common.months" to "Buwan", + "common.move" to "Ilipat", + "common.name" to "Pangalan", + "common.new" to "Bago", + "common.next" to "Susunod", + "common.no" to "Hindi", + "common.ok" to "OK", + "common.optional" to "Opsyonal", + "common.period" to "Panahon", + "common.previous" to "Nakaraan", + "common.refresh" to "I-reload", + "common.remove" to "Alisin", + "common.required" to "Kinakailangan", + "common.reset" to "I-reset", + "common.save" to "I-save", + "common.saved" to "Na-save", + "common.saving" to "Sine-save...", + "common.search" to "Maghanap", + "common.select" to "Pumili", + "common.status" to "Katayuan", + "common.submit" to "Ipasa", + "common.time" to "Oras", + "common.today" to "Ngayon", + "common.type" to "Uri", + "common.update" to "I-update", + "common.view" to "Tingnan", + "common.weeks" to "Linggo", + "common.years" to "Taon", + "common.yes" to "Oo", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Magdagdag ng palo", + "courtDrawingTool.backhand" to "Backhand", + "courtDrawingTool.codeLabel" to "Code", + "courtDrawingTool.completeFirstStroke" to "Tapusin ang unang palo", + "courtDrawingTool.completeFirstStrokeHint" to "Piliin ang panimulang posisyon, side, spin at target. Pagkatapos ay lalabas ang graphic.", + "courtDrawingTool.configureExercise" to "I-configure ang ehersisyo", + "courtDrawingTool.counterSpin" to "Counter spin", + "courtDrawingTool.forehand" to "Forehand", + "courtDrawingTool.noAdditionalStrokes" to "Wala pang mga kasunod na palo.", + "courtDrawingTool.preview" to "Preview", + "courtDrawingTool.previewHint" to "Lilitaw ang graphic kapag kumpleto na ang unang palo.", + "courtDrawingTool.service" to "Serve:", + "courtDrawingTool.serviceTitle" to "Serve", + "courtDrawingTool.sidespin" to "Sidespin", + "courtDrawingTool.sideUnderspin" to "Side underspin", + "courtDrawingTool.spin" to "Spin:", + "courtDrawingTool.startLeft" to "kaliwa", + "courtDrawingTool.startMiddle" to "gitna", + "courtDrawingTool.startRight" to "kanan", + "courtDrawingTool.stepAdditionalStrokes" to "4. Mga kasunod na palo", + "courtDrawingTool.stepAdditionalStrokesHint" to "Opsyonal na magdagdag pa ng mga bola bilang listahan.", + "courtDrawingTool.stepFirstStroke" to "2. Unang palo", + "courtDrawingTool.stepFirstStrokeHint" to "Itakda ang side at spin para sa unang bola.", + "courtDrawingTool.stepStartPosition" to "1. Panimulang posisyon", + "courtDrawingTool.stepStartPositionHint" to "Saang posisyon ng serve magsisimula ang ehersisyo?", + "courtDrawingTool.stepTarget" to "3. Target", + "courtDrawingTool.stepTargetHint" to "Pumili ng target zone para sa unang palo.", + "courtDrawingTool.strokeSide" to "Side", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Chop defense", + "courtDrawingTool.strokeTypeCounter" to "Counter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Uri ng palo", + "courtDrawingTool.strokeTypeLobDefense" to "Lob defense", + "courtDrawingTool.strokeTypePush" to "Push", + "courtDrawingTool.strokeTypeSmash" to "Smash", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "backhand half-long", + "courtDrawingTool.targetBackhandLong" to "backhand long", + "courtDrawingTool.targetBackhandShort" to "backhand short", + "courtDrawingTool.targetForehandHalfLong" to "forehand half-long", + "courtDrawingTool.targetForehandLong" to "forehand long", + "courtDrawingTool.targetForehandShort" to "forehand short", + "courtDrawingTool.targetMiddleHalfLong" to "middle half-long", + "courtDrawingTool.targetMiddleLong" to "middle long", + "courtDrawingTool.targetMiddleShort" to "middle short", + "courtDrawingTool.targetPosition" to "Posisyon ng target:", + "courtDrawingTool.targetPositionLabel" to "Posisyon ng target", + "courtDrawingTool.title" to "Guhit ng ehersisyo sa table tennis", + "courtDrawingTool.titleLabel" to "Pamagat", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "papunta sa", + "courtDrawingTool.underspin" to "Underspin", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Aktibong araw ng pagsasanay", + "diary.activities" to "Mga aktibidad", + "diary.activity" to "Aktibidad", + "diary.activityDrawing" to "Guhit ng aktibidad", + "diary.activityImage" to "Larawan ng aktibidad", + "diary.activityNotFound" to "Hindi nahanap ang aktibidad. Mangyaring pumili mula sa listahan.", + "diary.activityOrTimeblock" to "Aktibidad / time block", + "diary.activityPlaceholder" to "Aktibidad", + "diary.activityRequired" to "Mangyaring maglagay ng aktibidad.", + "diary.addActivity" to "Magdagdag ng aktibidad", + "diary.addGroup" to "Magdagdag ng grupo", + "diary.addGroupActivity" to "Magdagdag ng aktibidad ng grupo", + "diary.addGroupButton" to "+ Grupo", + "diary.addTimeblock" to "Time block", + "diary.all" to "Lahat", + "diary.applySuggestion" to "Ilapat ang mungkahi", + "diary.assignParticipants" to "Magtalaga ng mga kalahok", + "diary.assignParticipantsForGroupActivity" to "Magtalaga ng mga kalahok para sa aktibidad ng grupo", + "diary.assignShort" to "Italaga", + "diary.bookAccident" to "Magtala ng aksidente", + "diary.confirmDelete" to "Kumpirmahin ang pagbura", + "diary.confirmDeleteDate" to "Sigurado ka bang buburahin ang petsang ito?", + "diary.confirmDeleteDateDetails" to "Mabubura rin ang lahat ng kaugnay na datos.", + "diary.confirmDeleteGroup" to "Sigurado ka bang buburahin ang grupong \"{name}\"?", + "diary.createDate" to "Lumikha ng petsa", + "diary.createDrawing" to "Gumawa ng guhit ng ehersisyo", + "diary.createGroups" to "Lumikha ng mga grupo", + "diary.createNew" to "Lumikha ng bago", + "diary.createNewDate" to "Lumikha ng bagong petsa", + "diary.date" to "Petsa", + "diary.dateCannotBeDeleted" to "Hindi mabubura ang petsa", + "diary.dateCannotBeDeletedDetails" to "May laman pa (plano ng pagsasanay, mga kalahok, aktibidad, aksidente o mga tala).", + "diary.dateNoLongerCurrent" to "Hindi na kasalukuyan ang napiling petsa. Pakisubukan muli.", + "diary.delete" to "Burahin", + "diary.deleteDate" to "Burahin ang petsa", + "diary.deleteGroup" to "Burahin ang grupo", + "diary.duration" to "Tagal", + "diary.durationExampleLong" to "hal. 2x7 o 3*5", + "diary.durationExampleShort" to "hal. 2x7", + "diary.durationMinutes" to "Tagal (min)", + "diary.editActivity" to "I-edit ang aktibidad", + "diary.editGroupActivity" to "I-edit ang aktibidad ng grupo", + "diary.editTrainingTimes" to "I-edit ang oras ng pagsasanay", + "diary.errorCreatingActivity" to "Error sa paggawa ng aktibidad", + "diary.errorCreatingGroups" to "Error sa paggawa ng mga grupo", + "diary.errorDeletingGroup" to "Error sa pagbura ng grupo", + "diary.errorLoadingPredefinedActivities" to "Error sa pag-load ng paunang natukoy na mga aktibidad", + "diary.errorMarkingForm" to "Error sa pagmamarka ng pormularyo ng kasapi", + "diary.errorOccurred" to "May naganap na error. Pakisubukang muli.", + "diary.existingGroups" to "Mga kasalukuyang grupo", + "diary.filterAbsent" to "Liban", + "diary.filterAll" to "Lahat", + "diary.filterExcused" to "May paalam", + "diary.filterPresent" to "Dumalo", + "diary.filterTest" to "Trial", + "diary.formHandedOver" to "Naibigay na ang pormularyo ng kasapi", + "diary.formMarkedAsHandedOver" to "Namarkahan bilang naibigay na ang pormularyo ng kasapi", + "diary.freeActivities" to "Libreng aktibidad", + "diary.gallery" to "Gallery ng mga kasapi", + "diary.galleryCreating" to "Ginagawa ang gallery…", + "diary.group" to "Grupo...", + "diary.groupDeletedSuccessfully" to "Matagumpay na nabura ang grupo!", + "diary.groupManagement" to "Pamamahala ng grupo", + "diary.groupsCreated" to "Matagumpay na nalikha ang {count} grupo!", + "diary.groupsLabel" to "Mga grupo", + "diary.groupsSection" to "Mga grupo", + "diary.leader" to "Tagapanguna", + "diary.min" to "Min", + "diary.minutes" to "Minuto", + "diary.mustCreateAtLeastTwoGroups" to "Sa unang paggawa, kailangan ng hindi bababa sa 2 grupo!", + "diary.nextAppointment" to "Susunod na schedule", + "diary.noActiveTrainingDay" to "Walang napiling araw ng pagsasanay.", + "diary.noEntries" to "Walang entry", + "diary.noFreeActivitiesYet" to "Wala pang naitalang libreng aktibidad.", + "diary.noParticipants" to "Walang kalahok para sa araw ng pagsasanay na ito.", + "diary.numberOfGroups" to "Bilang ng grupo", + "diary.oneGroupAdded" to "Matagumpay na naidagdag ang 1 grupo!", + "diary.openPlanItems" to "{count} bukas", + "diary.openPlanItemsLabel" to "Katayuan ng plano", + "diary.overallActivity" to "Kabuuang aktibidad", + "diary.participants" to "Mga kalahok", + "diary.participantStatusCancelled" to "Kinansela", + "diary.participantStatusExcused" to "May paalam", + "diary.participantStatusNone" to "Walang katayuan", + "diary.planActivitiesCount" to "Mga aktibidad sa plano", + "diary.planAddHint" to "Magdagdag ng bagong item sa plano gamit ang mga aksyon sa itaas.", + "diary.planEmptyState" to "Wala pang nakalagay sa plano ng pagsasanay.", + "diary.quickAdd" to "+ Quick add", + "diary.searchParticipants" to "Maghanap ng kalahok", + "diary.selectGroup" to "Pumili ng grupo...", + "diary.selectGroupAndActivity" to "Mangyaring pumili ng grupo at maglagay ng aktibidad.", + "diary.selectParticipantAndNote" to "Mangyaring pumili ng kalahok at maglagay ng tala.", + "diary.selectTags" to "Pumili ng mga tag", + "diary.selectTrainingGroup" to "Pumili ng grupo ng pagsasanay", + "diary.selectTrainingGroupPlaceholder" to "Mangyaring pumili...", + "diary.showImage" to "Ipakita ang larawan/guhit", + "diary.skipSuggestion" to "Magpatuloy nang walang mungkahi", + "diary.standardActivities" to "Mga karaniwang aktibidad", + "diary.standardActivityAddError" to "Hindi maidagdag ang karaniwang aktibidad.", + "diary.standardDurationShort" to "min", + "diary.startTime" to "Oras ng simula", + "diary.statusEmpty" to "Wala pang laman ang araw ng pagsasanay na ito.", + "diary.statusInProgress" to "Bahagyang naihanda ang araw ng pagsasanay na ito.", + "diary.statusOpenShort" to "Bukas", + "diary.statusReady" to "Naayos na ang oras at plano ng pagsasanay.", + "diary.statusReadyShort" to "Handa", + "diary.suggestion" to "Mungkahi", + "diary.timeblock" to "Time block", + "diary.timeblocksCount" to "Mga time block", + "diary.title" to "Talaarawan ng pagsasanay", + "diary.today" to "Ngayon", + "diary.trainingDayAsPDF" to "I-download ang araw ng pagsasanay bilang PDF", + "diary.trainingDayAsPDFShort" to "Araw ng pagsasanay PDF", + "diary.trainingDayChecklist" to "Checklist ng pagkumpleto", + "diary.trainingDaySection" to "Araw ng pagsasanay", + "diary.trainingDaySummaryPdfShort" to "Buod ng kalahok bilang PDF", + "diary.trainingEnd" to "Pagtatapos ng pagsasanay", + "diary.trainingPlan" to "Plano ng pagsasanay", + "diary.trainingPlanAsPDF" to "Plano ng pagsasanay bilang PDF", + "diary.trainingPlanPdfShort" to "Flow plan bilang PDF", + "diary.trainingStart" to "Simula ng pagsasanay", + "diary.trainingTimesUpdated" to "Matagumpay na na-update ang oras ng pagsasanay.", + "diary.trainingWindow" to "Oras ng pagsasanay", + "diary.trainingWindowUnset" to "Hindi pa naitakda", + "diary.unassignedPlanItems" to "{count} hindi nakatalaga", + "diary.updateTimes" to "I-update ang oras", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Aleman)", + "languages.de-CH" to "Schwiizerdütsch (Swiss Aleman)", + "languages.en-AU" to "English (Ingles (AU))", + "languages.en-GB" to "English (Ingles (GB))", + "languages.en-US" to "English (Ingles (US))", + "languages.es" to "Español (Espanyol)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (Pranses)", + "languages.it" to "Italiano (Italyano)", + "languages.ja" to "日本語 (Hapones)", + "languages.pl" to "Polski (Polako)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Intsik)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Mga aksyon", + "members.active" to "Aktibo", + "members.activeMembers" to "Mga aktibong kasapi", + "members.addEmail" to "Magdagdag ng email address", + "members.addGroup" to "Magdagdag ng grupo...", + "members.addPhone" to "Magdagdag ng numero ng telepono", + "members.address" to "Address", + "members.adultReleaseApproved" to "Aprubado para sa matatanda", + "members.adultReserveApproved" to "Adult reserve", + "members.adults" to "Adulto (20+)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "mula", + "members.ageGroup" to "Pangkat ng edad", + "members.ageRange" to "Edad mula - hanggang", + "members.ageToPlaceholder" to "hanggang", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "Petsa ng kapanganakan", + "members.bulkActions" to "Mga bulk action", + "members.camera" to "Camera", + "members.change" to "Baguhin", + "members.city" to "Lungsod", + "members.clearFields" to "Burahin ang mga field", + "members.clearFilters" to "I-reset ang mga filter", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "Lumikha", + "members.createNewMember" to "Lumikha ng bagong kasapi", + "members.dataIssueAddress" to "Kulang ang address", + "members.dataIssueBirthdate" to "Kulang ang petsa ng kapanganakan", + "members.dataIssueCity" to "Kulang ang lungsod", + "members.dataIssueEmail" to "Kulang ang email", + "members.dataIssueGender" to "Hindi malinaw ang kasarian", + "members.dataIssuePhone" to "Kulang ang telepono", + "members.dataIssuePostalCode" to "Kulang ang postal code", + "members.dataIssueStreet" to "Kulang ang kalye", + "members.dataIssueTrainingGroup" to "Kulang ang grupo ng pagsasanay", + "members.dataQuality" to "Kalidad ng datos", + "members.dataQualityComplete" to "Kumpleto ang datos", + "members.deactivateMember" to "I-deactivate ang kasapi", + "members.deactivateMemberConfirm" to "Sigurado ka bang ide-deactivate si \"{name}\"?", + "members.deactivateMemberTitle" to "I-deactivate ang kasapi", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "I-edit ang kasapi", + "members.editorAssignTrainingGroupHint" to "Mangyaring magtalaga ng kahit isang grupo ng pagsasanay.", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "Inirerekomendang entry", + "members.emailAddress" to "Email address", + "members.emailAddressShort" to "Email", + "members.emails" to "Mga email address", + "members.errorAddingToGroup" to "Error sa pagdagdag sa grupo", + "members.errorDeactivatingMember" to "Error sa pag-deactivate ng miyembro", + "members.errorDeletingImage" to "Hindi mabura ang larawan", + "members.errorLoadingImage" to "Error sa pag-load ng larawan", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "Error sa pag-load ng paglahok sa pagsasanay", + "members.errorMarkingForm" to "Error sa pagmamarka ng form.", + "members.errorRemovingFromGroup" to "Error sa pag-alis mula sa grupo", + "members.errorRemovingTestMembership" to "Error sa pag-alis ng trial membership.", + "members.errorRotatingImage" to "Error sa pag-ikot ng larawan", + "members.errorSavingMember" to "Error sa pag-save ng miyembro", + "members.errorSettingPrimaryImage" to "Hindi maitakda ang pangunahing larawan", + "members.errorTransfer" to "Error sa paglipat", + "members.errorUpdatingRatings" to "Error sa pag-update ng mga TTR/QTTR value", + "members.errorUploadingImage" to "Hindi ma-upload ang larawan", + "members.exercises" to "Mga ehersisyo", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "Pangalan", + "members.formHandedOver" to "Naibigay na ang pormularyo ng kasapi", + "members.formMarkedAsHandedOver" to "Namarkahan bilang naibigay na ang pormularyo ng kasapi.", + "members.gender" to "Kasarian", + "members.genderDiverse" to "Iba pa", + "members.genderFemale" to "Babae", + "members.genderMale" to "Lalaki", + "members.genderUnknown" to "Hindi alam", + "members.generatePhoneList" to "Gumawa ng listahan ng telepono", + "members.groupPhotoCrop" to "Iproseso ang group photo", + "members.groupPhotoCropSaved" to "Na-save ang larawan ng miyembro mula sa group photo.", + "members.image" to "Larawan", + "members.imageDeleted" to "Nabura na ang larawan.", + "members.imageInternet" to "Larawan (net?)", + "members.imagePreview" to "Preview ng larawan ng miyembro", + "members.imageUpdated" to "Na-update na ang larawan", + "members.inactive" to "hindi aktibo", + "members.inactiveMembers" to "Mga hindi aktibong kasapi", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17 (17 pababa)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Apelyido", + "members.lastTrainingFilter" to "Huling training", + "members.lastTrainingFilterHasDate" to "May petsa", + "members.lastTrainingFilterHint" to "Table: kasalukuyang season sa AK. Buong detalye (dalawang season, huling training, participations) kapag-hover sa hilera.", + "members.lastTrainingFilterNoDate" to "Walang huling training", + "members.lastTrainingFilterNotInTraining" to "Flag na “hindi na nagte-training”", + "members.loadingMembers" to "Loading members...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "Na-deactivate na ang kasapi", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "Naibigay na ang pormularyo ng kasapi", + "members.memberImage" to "Larawan ng kasapi", + "members.memberImages" to "Mga larawan ng miyembro", + "members.memberInfo" to "Impormasyon ng kasapi", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "Walang naka-save na myTischtennis ID", + "members.name" to "Apelyido, Pangalan", + "members.newMember" to "Bagong kasapi", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "Walang grupong nakatalaga", + "members.noGroupsAvailable" to "Walang available na grupo", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "Mga tala", + "members.noTestMembership" to "Hindi na trial member", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "Tanging mga aktibong miyembro lamang ang ilalabas", + "members.openTasks" to "Open tasks", + "members.parent" to "Magulang", + "members.parentFallback" to "Parent", + "members.parentName" to "Pangalan (hal. ina, ama)", + "members.phoneList" to "ListahanNgTelepono.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "Numero ng telepono", + "members.phoneNumberShort" to "Telepono", + "members.phones" to "Mga numero ng telepono", + "members.picsInInternetAllowed" to "Pinapayagan ang larawan sa internet", + "members.postalCode" to "Postal code", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "Pangunahin", + "members.primaryImageUpdated" to "Na-update na ang pangunahing larawan.", + "members.remove" to "Alisin", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "Aktibo + hindi kumpleto ang datos", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "Hindi kumpleto ang datos", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "Paghahanap at mga filter", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "Pumili ng file", + "members.showInactiveMembers" to "Ipakita ang mga hindi aktibong miyembro", + "members.showTrainingParticipationsColumn" to "Ipakita ang column na « Training participations »", + "members.showTtrHistory" to "Ipakita ang TTR history", + "members.sixOrMoreParticipations" to "6 o higit pang paglahok sa pagsasanay", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "Halaga ng QTTR", + "members.status" to "Katayuan", + "members.street" to "Kalye", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "Buksan", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "Magtalaga ng grupo ng pagsasanay", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "Suriin ang kalidad ng datos", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "Trial", + "members.testMembers" to "Mga kasaping pansubok", + "members.testMembership" to "Trial membership", + "members.testMembershipRemoved" to "Inalis na ang trial membership.", + "members.threeOrMoreParticipations" to "3 o higit pang paglahok sa pagsasanay", + "members.title" to "Mga kasapi", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "Mga grupo ng pagsasanay", + "members.trainingParticipations" to "Paglahok sa pagsasanay", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "Ilipat ang mga miyembro", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "Adulto (hindi youth sa cutoff)", + "members.ttAgeClassCol" to "Edad (TT)", + "members.ttFilterGroupJ" to "Babae at lalaki (halo)", + "members.ttFilterGroupM" to "Babae lamang", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "kasalukuyan", + "members.ttSeasonFilter" to "Season (cutoff)", + "members.ttSeasonNextTag" to "susunod", + "members.ttStichtagHint" to "Cutoff Enero 1 (DTTB). Lalaki: J lang. Babae: J at M.", + "members.updateRatings" to "I-update ang TTR/QTTR mula sa myTischtennis", + "members.updating" to "Nag-a-update...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "Gustong maglaro", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Kanselahin", + "messages.confirm" to "Kumpirmahin", + "messages.error" to "Error", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Impormasyon", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Tagumpay", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Babala", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Mga pag-apruba", + "navigation.backToHome" to "Bumalik sa home", + "navigation.billing" to "Pagsingil", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Mga setting ng club", + "navigation.clubTournaments" to "Mga paligsahan ng club", + "navigation.competitions" to "Mga kompetisyon", + "navigation.dailyBusiness" to "Araw-araw na negosyo", + "navigation.diary" to "Talaarawan", + "navigation.home" to "Home", + "navigation.login" to "Mag-login", + "navigation.logout" to "Mag-logout", + "navigation.logs" to "Mga system log", + "navigation.members" to "Mga miyembro", + "navigation.memberTransfer" to "Paglipat ng miyembro", + "navigation.myTischtennisAccount" to "myTischtennis Account", + "navigation.orders" to "Mga order", + "navigation.permissions" to "Mga pahintulot", + "navigation.personalSettings" to "Mga personal na setting", + "navigation.predefinedActivities" to "Mga paunang natukoy na aktibidad", + "navigation.register" to "Magrehistro", + "navigation.schedule" to "Mga iskedyul", + "navigation.settings" to "Mga setting", + "navigation.statistics" to "Istatistika ng pagsasanay", + "navigation.teamManagement" to "Pamamahala ng team", + "navigation.tournamentParticipations" to "Mga paglahok sa paligsahan", + "navigation.tournaments" to "Mga paligsahan", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Gumawa ng order", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Halaga", + "orders.dateAutoHint" to "Awtomatikong itinatakda ang petsa at bawat pagbabago ay sine-save kasama ang petsa.", + "orders.errorLoading" to "Hindi ma-load ang mga order.", + "orders.errorSaving" to "Hindi ma-save ang order.", + "orders.filterAllClubs" to "Lahat ng club", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Lahat ng status", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Dito makikita at mapapamahalaan ang lahat ng order mula sa lahat ng club.", + "orders.globalTitle" to "Mga order ng lahat ng club", + "orders.history" to "Kasaysayan", + "orders.item" to "Item", + "orders.itemPlaceholder" to "hal. jersey, hoodie, o lagayan ng raketa", + "orders.loading" to "Nilo-load ang mga order...", + "orders.member" to "Miyembro", + "orders.memberTitle" to "Mga order: {name}", + "orders.noOrdersGlobal" to "Wala pang mga order sa ngayon.", + "orders.noOrdersMember" to "Wala pang order para sa miyembrong ito.", + "orders.open" to "Natitira", + "orders.orderDate" to "Ginawa noong", + "orders.paid" to "Bayad", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Maghanap ayon sa club, miyembro, o item", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "dumating na ang item", + "orders.statusDate" to "Huling pagbabago", + "orders.statusHandedOver" to "naibigay na ang item", + "orders.statusOrdered" to "na-order na", + "orders.statusRequested" to "ninanais", + "orders.title" to "Mga order", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Nilikha:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Liga:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Registration para sa:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Plano na liga:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Season:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Age group ng koponan:", + "pdfGenerator.teamGenderLabel" to "Kasarian ng koponan:", + "pdfGenerator.teamLineupTitle" to "Line-up ng koponan", + "pdfGenerator.teamNameLabel" to "Koponan:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Wika", + "settings.languageChanged" to "Matagumpay na nagbago ang wika", + "settings.languageDescription" to "Pumili ng iyong gustong wika para sa aplikasyon", + "settings.personalSettings" to "Mga personal na setting", + "settings.selectLanguage" to "Pumili ng wika", + "settings.title" to "Mga setting", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Magagamit na manlalaro", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Pagiging karapat-dapat", + "teamManagement.eligibilityAdultRelease" to "Pinayagan sa adults", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Pinayagan + reserve sa adults", + "teamManagement.eligibilityAdultReserve" to "Reserve sa adults", + "teamManagement.eligibilityRegular" to "Karaniwan", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "I-export ang line-up bilang PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Unang yugto", + "teamManagement.firstHalfFull" to "Unang yugto (Hulyo - Disyembre)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Mangyaring pumili ng liga at i-save.", + "teamManagement.lineupEmpty" to "Wala pang manlalarong itinalaga sa koponang ito.", + "teamManagement.lineupPdfEmpty" to "Walang manlalaro sa line-up – hindi makagawa ng PDF.", + "teamManagement.lineupPdfFilePrefix" to "Line-up", + "teamManagement.lineupProposal" to "Line-up ayon sa QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Hindi mai-save ang line-up ng koponan.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "Mahigit 30 QTTR points ang lamang ni {higher} kay {lower}. Pakitama ang ayos na ito.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Nawawalang impormasyon:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "I-paste at i-parse ang URL ng MyTischtennis para i-link ang team ID at datos ng liga.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Plano na liga", + "teamManagement.plannedLeagueHint" to "Opsyonal. Hiwalay sa nakarehistrong liga (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "hal. district league, pagkatapos ng susunod na rehistro …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Ikalawang yugto", + "teamManagement.secondHalfFull" to "Ikalawang yugto (mula Enero 1)", + "teamManagement.selectedLineup" to "Napiling manlalaro", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Magdagdag ng pool rule", + "tournaments.address" to "Address", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Kumpleto na ang lahat ng datos ng mga kalahok.", + "tournaments.allDataCompleteTop3" to "Kumpleto na ang lahat ng datos ng unang 3 puwesto.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Mangyaring magdagdag ng kahit isang pool rule para sa {label} (hal. places 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Itama", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Gumawa ng final round mula sa intermediate round", + "tournaments.createFinalFromPreliminary" to "Gumawa ng final round mula sa preliminary round", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Gumawa ng intermediate round mula sa preliminary round", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Hindi pa naitala", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Sigurado ka bang buburahin ang class na \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "Aalisin ang lahat ng participants sa class na ito.", + "tournaments.deleteClassTitle" to "Burahin ang class", + "tournaments.deleteExistingPairingsConfirm" to "Buburahin ang mga kasalukuyang pairing. Magpatuloy?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Ipamahagi ang mga bakanteng mesa", + "tournaments.distributeTablesResult" to "Pamamahagi ng mesa", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Suitable for: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Mangyaring maglagay ng pangalan ng class.", + "tournaments.enterExternalParticipantName" to "Mangyaring ilagay kahit first name at last name.", + "tournaments.enterMiniLocation" to "Mangyaring maglagay ng lokasyon.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "May error sa pamamahagi ng mga mesa.", + "tournaments.errorGeneratingPdf" to "Error sa pagbuo ng PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Final round", + "tournaments.finishMatch" to "Tapusin", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Gumagawa ng PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Mga grupo", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Intermediate round (round 2)", + "tournaments.internalStatsAbsoluteRank" to "Ranggo sa kabuuang marka", + "tournaments.internalStatsAgeFilter" to "Edad at kasarian (singles)", + "tournaments.internalStatsAgeFilterAll" to "Lahat ng pangkat ng edad", + "tournaments.internalStatsAgeFilterNone" to "Walang napiling pangkat ng edad", + "tournaments.internalStatsAgeNoClass" to "Walang klase", + "tournaments.internalStatsAgeSelectAll" to "Lahat", + "tournaments.internalStatsAgeSelectNone" to "Wala", + "tournaments.internalStatsAverageRank" to "Ranggo sa average (bawat paligsahan)", + "tournaments.internalStatsAvgPoints" to "Avg.", + "tournaments.internalStatsEmpty" to "Walang datos sa napiling panahon.", + "tournaments.internalStatsExportPdf" to "I-export bilang PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Huling 12 buwan", + "tournaments.internalStatsLast3Months" to "Huling 3 buwan", + "tournaments.internalStatsLast6Months" to "Huling 6 na buwan", + "tournaments.internalStatsOpenButton" to "Buksan ang estadistika (singles)", + "tournaments.internalStatsPeriod" to "Saklaw", + "tournaments.internalStatsPoints" to "Kabuuan", + "tournaments.internalStatsPointsExplain" to "Marka: sa bawat grupo ang puwesto ay nasa porsyento (may N na may ranggo: 1 = 100%, huli = 0%, linear sa gitna; parehong ranggo = parehong halaga). Kinakabilang ang lahat ng may ranggo sa grupo (kasama ang bisita). Isang manlalaro lamang: 100%. Sa KO: pinakamataas na grupo ng klase + 1, +1 bawat panalong laro sa KO. Mga miyembro lamang (singles).", + "tournaments.internalStatsTitle" to "Estadistika ng internal na paligsahan (singles)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} paligsahan sa panahon (hindi kasama ang mini championships).", + "tournaments.internalStatsTtAdult" to "Adults", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Markahan bilang live", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Kailangan ng hindi bababa sa 2 participants para sa pairings.", + "tournaments.missingDataPDF" to "Nawawalang datos bilang PDF", + "tournaments.missingDataPDFSubtitle" to "Pakikolekta ang nawawalang datos (may markang ____) mula sa mga kalahok at itala dito.", + "tournaments.missingDataPDFSubtitleTop3" to "Pakikolekta ang nawawalang datos ng unang 3 (may markang ____) at itala dito.", + "tournaments.missingDataPDFTitle" to "Nawawalang datos ng mga kalahok – Mini championship", + "tournaments.missingDataPDFTitleTop3" to "Nawawalang datos – Top 3 Mini championship", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Bagong set, hal. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Walang laban kung saan pareho ang mga manlalaro ay bakante.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "No suitable class found.", + "tournaments.noFreeTables" to "Walang bakanteng mesa.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Walang nahanap na orphaned matches.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Wala pang rules. Halimbawa: places 1 at 2 -> top round-2 groups.", + "tournaments.noPoolRulesYetFinal" to "Wala pang rules. Halimbawa: places 1 at 2 -> final round.", + "tournaments.noTop3Yet" to "Hindi pa natutukoy ang unang 3 puwesto.", + "tournaments.noTournamentDate" to "Walang tournament date.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Walang training session para sa {date}.", + "tournaments.noTrainingParticipants" to "Walang participants na nakita sa training session para sa petsang ito.", + "tournaments.noValidTrainingParticipants" to "Walang valid na participants na nakita sa training session para sa petsang ito.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Bilang ng mesa", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} orphaned matches ang naalis.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Pahina", + "tournaments.pairingAlreadyExists" to "May ganitong pairing na.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} pairings ang nagawa, {errorCount} errors.", + "tournaments.participantConflicts" to "Conflicts", + "tournaments.participantNotFound" to "Hindi nakita ang participant.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Telepono", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Mga puwesto mula sa bawat grupo (hal. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Manlalaro 1", + "tournaments.playerTwo" to "Manlalaro 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Maglaro para sa ikatlong puwesto", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Advance: preliminary round → intermediate round (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Advance: round {from} → round {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Ranggo", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Bilang ng mga grupo", + "tournaments.roundMode" to "Mode", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "I-save ang rounds", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Mangyaring pumili muna ng tournament.", + "tournaments.selectTwoDifferentPlayers" to "Mangyaring pumili ng dalawang magkaibang player.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Hindi wastong tugon mula sa server.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "Opsyonal ang intermediate round. Kapag in-activate mo ito, laging may susunod na final round. Ang knockout final round ay gagawin bilang isang bracket.", + "tournaments.stageConfigLoadError" to "Error sa pag-load ng round configuration.", + "tournaments.stageConfigLoading" to "Naglo-load ng round configuration…", + "tournaments.stageConfigMissingIds" to "Hindi ma-save: kulang ang club o tournament ID.", + "tournaments.stageConfigTitle" to "Intermediate at Final Round", + "tournaments.stageCreated" to "Nagawa ang round {round}.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Tapos", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Bukas", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Mesa", + "tournaments.tablesDistributed" to "Ang mga mesa ay naipamahagi na.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Target", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Target na bilang ng grupo", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Babae", + "tournaments.tournamentClassGenderOpen" to "Bukas (lahat)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Alisin ang live marker", + "tournaments.unpairedDoubles" to "Doubles without partner", + "tournaments.useIntermediateStage" to "Gamitin ang intermediate round", + "tournaments.warningBirthDateMissing" to "Birth date is missing for this class", + "tournaments.warningClassMissing" to "Class not found", + "tournaments.warningGenderMismatch" to "Gender does not match class", + "tournaments.warningGenderMissing" to "Gender is missing for this class", + "tournaments.warningMissingPairing" to "Doubles partner missing", + "tournaments.warningTooOldForClass" to "Too old for this class", + "tournaments.warningTooYoungForClass" to "Too young for this class", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Mga aksyon", + "trainingStats.activeMembers" to "Mga aktibong miyembro", + "trainingStats.allTrainingDays" to "Lahat ng araw ng pagsasanay", + "trainingStats.attendingMembers" to "Mga dumalong miyembro", + "trainingStats.averageParticipationCurrentMonth" to "Karaniwang paglahok (kasalukuyang buwan)", + "trainingStats.averageParticipationHalfYear" to "Karaniwang paglahok (kalahating taon)", + "trainingStats.averageParticipationLastMonth" to "Karaniwang paglahok (nakaraang buwan)", + "trainingStats.averageParticipationQuarter" to "Karaniwang paglahok (quarter)", + "trainingStats.averageParticipationYear" to "Karaniwang paglahok (taon)", + "trainingStats.birthdate" to "Petsa ng kapanganakan", + "trainingStats.date" to "Petsa", + "trainingStats.lastTraining" to "Huling pagsasanay", + "trainingStats.memberParticipations" to "Paglahok ng miyembro", + "trainingStats.name" to "Pangalan", + "trainingStats.noParticipants" to "Walang kalahok", + "trainingStats.participants" to "Mga kalahok", + "trainingStats.participations12Months" to "Paglahok (12 buwan)", + "trainingStats.participations3Months" to "Paglahok (3 buwan)", + "trainingStats.participationsTotal" to "Paglahok (kabuuan)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Ipakita ang mga detalye", + "trainingStats.title" to "Mga istatistika ng pagsasanay", + "trainingStats.trainingDayFilter" to "Araw ng pagsasanay", + "trainingStats.trainingDays" to "Mga araw ng pagsasanay (huling 12 buwan)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Araw ng linggo", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val fr: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Journal d'entraînement", + "app.title" to "Journal d'entraînement", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Activation échouée. Veuillez vérifier le lien.", + "auth.confirmPassword" to "Confirmer le mot de passe", + "auth.email" to "E-mail", + "auth.forgotPassword" to "Mot de passe oublié ?", + "auth.forgotPasswordDescription" to "Entrez votre adresse e-mail. Vous recevrez un lien pour réinitialiser votre mot de passe.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Connexion", + "auth.loginFailed" to "Connexion échouée. Veuillez vérifier vos identifiants.", + "auth.loginSuccess" to "Connexion réussie", + "auth.logout" to "Déconnexion", + "auth.logoutSuccess" to "Déconnexion réussie", + "auth.newPassword" to "Nouveau mot de passe", + "auth.noAccount" to "Pas encore de compte ?", + "auth.password" to "Mot de passe", + "auth.passwordResetSuccess" to "Votre mot de passe a été modifié avec succès. Vous pouvez maintenant vous connecter.", + "auth.passwordsDoNotMatch" to "Les mots de passe ne correspondent pas.", + "auth.passwordTooShort" to "Le mot de passe doit contenir au moins 6 caractères.", + "auth.register" to "S'inscrire", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Se souvenir de moi", + "auth.resetEmailSent" to "Si un compte avec cette adresse existe, un lien de réinitialisation a été envoyé. Vérifiez votre boîte de réception (et les spams).", + "auth.resetFailed" to "Le mot de passe n'a pas pu être modifié. Le lien a peut-être expiré.", + "auth.resetPassword" to "Définir un nouveau mot de passe", + "auth.resetRequestFailed" to "Demande échouée. Veuillez réessayer.", + "auth.saveNewPassword" to "Enregistrer le mot de passe", + "auth.saving" to "Enregistrement...", + "auth.sending" to "Envoi en cours...", + "auth.sendResetLink" to "Envoyer le lien", + "auth.sessionExpired" to "Votre session a expiré. Vous allez être déconnecté.", + "auth.toLogin" to "Vers la connexion", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Accès au club refusé.", + "club.accessRequested" to "L'accès a été demandé.", + "club.accessRequestFailed" to "La demande d'accès n'a pas pu être envoyée.", + "club.accessRequestPending" to "L'accès à ce club a été demandé. Veuillez patienter.", + "club.create" to "Créer un club", + "club.createTitle" to "Créer un club", + "club.diary" to "Journal d'entraînement", + "club.errorLoadingRequests" to "Erreur lors du chargement des demandes ouvertes", + "club.load" to "Charger", + "club.members" to "Membres", + "club.mobileSelectHint" to "Veuillez d'abord sélectionner un club pour utiliser l'application sur smartphone.", + "club.name" to "Nom du club", + "club.new" to "Nouveau club", + "club.noAccess" to "Vous n'avez pas encore accès à ce club.", + "club.openAccessRequests" to "Demandes d'accès ouvertes", + "club.openRequests" to "Demandes d'accès ouvertes", + "club.requestAccess" to "Demander l'accès", + "club.select" to "Sélectionner un club", + "club.selectPlaceholder" to "Choisir un club...", + "club.title" to "Club", + "club.trainingDiary" to "Journal d'entraînement", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Actions", + "common.active" to "Actif", + "common.add" to "Ajouter", + "common.all" to "Tous", + "common.apply" to "Appliquer", + "common.back" to "Retour", + "common.cancel" to "Annuler", + "common.choose" to "Choisir", + "common.clear" to "Effacer", + "common.close" to "Fermer", + "common.confirm" to "Confirmer", + "common.create" to "Créer", + "common.date" to "Date", + "common.days" to "jours", + "common.delete" to "Supprimer", + "common.description" to "Description", + "common.details" to "Détails", + "common.disabled" to "Désactivé", + "common.download" to "Télécharger", + "common.edit" to "Modifier", + "common.enabled" to "Activé", + "common.filter" to "Filtrer", + "common.hours" to "heures", + "common.in" to "dans", + "common.inactive" to "Inactif", + "common.loading" to "Chargement...", + "common.min" to "min", + "common.minutes" to "minutes", + "common.months" to "mois", + "common.move" to "Déplacer", + "common.name" to "Nom", + "common.new" to "Nouveau", + "common.next" to "Suivant", + "common.no" to "Non", + "common.ok" to "OK", + "common.optional" to "Optionnel", + "common.period" to "Période", + "common.previous" to "Précédent", + "common.refresh" to "Recharger", + "common.remove" to "Retirer", + "common.required" to "Obligatoire", + "common.reset" to "Réinitialiser", + "common.save" to "Enregistrer", + "common.saved" to "Enregistré", + "common.saving" to "Enregistrement...", + "common.search" to "Rechercher", + "common.select" to "Sélectionner", + "common.status" to "Statut", + "common.submit" to "Valider", + "common.time" to "Heure", + "common.today" to "Aujourd'hui", + "common.type" to "Type", + "common.update" to "Mettre à jour", + "common.view" to "Afficher", + "common.weeks" to "semaines", + "common.years" to "années", + "common.yes" to "Oui", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Ajouter un coup", + "courtDrawingTool.backhand" to "Revers", + "courtDrawingTool.codeLabel" to "Code", + "courtDrawingTool.completeFirstStroke" to "Compléter le premier coup", + "courtDrawingTool.completeFirstStrokeHint" to "Choisissez la position de départ, le côté, l’effet et la cible. Le schéma apparaîtra ensuite.", + "courtDrawingTool.configureExercise" to "Configurer l'exercice", + "courtDrawingTool.counterSpin" to "Contre-effet", + "courtDrawingTool.forehand" to "Coup droit", + "courtDrawingTool.noAdditionalStrokes" to "Aucun coup suivant ajouté pour le moment.", + "courtDrawingTool.preview" to "Aperçu", + "courtDrawingTool.previewHint" to "Le schéma apparaît dès que le premier coup est entièrement configuré.", + "courtDrawingTool.service" to "Service :", + "courtDrawingTool.serviceTitle" to "Service", + "courtDrawingTool.sidespin" to "Effet latéral", + "courtDrawingTool.sideUnderspin" to "Latéral coupé", + "courtDrawingTool.spin" to "Effet :", + "courtDrawingTool.startLeft" to "gauche", + "courtDrawingTool.startMiddle" to "centre", + "courtDrawingTool.startRight" to "droite", + "courtDrawingTool.stepAdditionalStrokes" to "4. Coups suivants", + "courtDrawingTool.stepAdditionalStrokesHint" to "Ajouter si besoin d’autres balles sous forme de séquence.", + "courtDrawingTool.stepFirstStroke" to "2. Premier coup", + "courtDrawingTool.stepFirstStrokeHint" to "Définir le côté et la rotation du premier ballon.", + "courtDrawingTool.stepStartPosition" to "1. Position de départ", + "courtDrawingTool.stepStartPositionHint" to "Depuis quelle position de service l'exercice commence-t-il ?", + "courtDrawingTool.stepTarget" to "3. Cible", + "courtDrawingTool.stepTargetHint" to "Choisir la zone cible pour le premier coup.", + "courtDrawingTool.strokeSide" to "Côté", + "courtDrawingTool.strokeTypeBlock" to "Bloc", + "courtDrawingTool.strokeTypeChopDefense" to "Défense coupée", + "courtDrawingTool.strokeTypeCounter" to "Contre", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Type de coup", + "courtDrawingTool.strokeTypeLobDefense" to "Défense lobée", + "courtDrawingTool.strokeTypePush" to "Poussette", + "courtDrawingTool.strokeTypeSmash" to "Frappe", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Revers mi-long", + "courtDrawingTool.targetBackhandLong" to "Revers long", + "courtDrawingTool.targetBackhandShort" to "Revers court", + "courtDrawingTool.targetForehandHalfLong" to "Coup droit mi-long", + "courtDrawingTool.targetForehandLong" to "Coup droit long", + "courtDrawingTool.targetForehandShort" to "Coup droit court", + "courtDrawingTool.targetMiddleHalfLong" to "Milieu mi-long", + "courtDrawingTool.targetMiddleLong" to "Milieu long", + "courtDrawingTool.targetMiddleShort" to "Milieu court", + "courtDrawingTool.targetPosition" to "Position cible :", + "courtDrawingTool.targetPositionLabel" to "Position cible", + "courtDrawingTool.title" to "Schéma d'exercice de tennis de table", + "courtDrawingTool.titleLabel" to "Titre", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "vers", + "courtDrawingTool.underspin" to "Coupe", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Jour d'entraînement actif", + "diary.activities" to "Activités", + "diary.activity" to "Activité", + "diary.activityDrawing" to "Schéma d'activité", + "diary.activityImage" to "Image d'activité", + "diary.activityNotFound" to "Activité introuvable. Veuillez en choisir une dans la liste.", + "diary.activityOrTimeblock" to "Activité / bloc horaire", + "diary.activityPlaceholder" to "Activité", + "diary.activityRequired" to "Veuillez saisir une activité.", + "diary.addActivity" to "Ajouter une activité", + "diary.addGroup" to "Ajouter un groupe", + "diary.addGroupActivity" to "Ajouter une activité de groupe", + "diary.addGroupButton" to "+ Groupe", + "diary.addTimeblock" to "Bloc horaire", + "diary.all" to "Tous", + "diary.applySuggestion" to "Appliquer la suggestion", + "diary.assignParticipants" to "Attribuer des participants", + "diary.assignParticipantsForGroupActivity" to "Attribuer des participants à l'activité de groupe", + "diary.assignShort" to "Attribuer", + "diary.bookAccident" to "Enregistrer un accident", + "diary.confirmDelete" to "Confirmer la suppression", + "diary.confirmDeleteDate" to "Voulez-vous vraiment supprimer cette date ?", + "diary.confirmDeleteDateDetails" to "Toutes les données associées seront également supprimées.", + "diary.confirmDeleteGroup" to "Voulez-vous vraiment supprimer le groupe \"{name}\" ?", + "diary.createDate" to "Créer la date", + "diary.createDrawing" to "Créer un schéma d'exercice", + "diary.createGroups" to "Créer des groupes", + "diary.createNew" to "Créer", + "diary.createNewDate" to "Créer une nouvelle date", + "diary.date" to "Date", + "diary.dateCannotBeDeleted" to "La date ne peut pas être supprimée", + "diary.dateCannotBeDeletedDetails" to "Du contenu existe encore (plan, participants, activités, accidents ou notes).", + "diary.dateNoLongerCurrent" to "La date sélectionnée n'était plus actuelle. Veuillez réessayer.", + "diary.delete" to "Supprimer", + "diary.deleteDate" to "Supprimer la date", + "diary.deleteGroup" to "Supprimer le groupe", + "diary.duration" to "Durée", + "diary.durationExampleLong" to "p. ex. 2x7 ou 3*5", + "diary.durationExampleShort" to "p. ex. 2x7", + "diary.durationMinutes" to "Durée (min)", + "diary.editActivity" to "Modifier l'activité", + "diary.editGroupActivity" to "Modifier l'activité de groupe", + "diary.editTrainingTimes" to "Modifier les horaires d'entraînement", + "diary.errorCreatingActivity" to "Erreur lors de la création de l'activité", + "diary.errorCreatingGroups" to "Erreur lors de la création des groupes", + "diary.errorDeletingGroup" to "Erreur lors de la suppression du groupe", + "diary.errorLoadingPredefinedActivities" to "Erreur lors du chargement des activités prédéfinies", + "diary.errorMarkingForm" to "Erreur lors du marquage du formulaire", + "diary.errorOccurred" to "Une erreur est survenue. Veuillez réessayer.", + "diary.existingGroups" to "Groupes existants", + "diary.filterAbsent" to "Absent", + "diary.filterAll" to "Tous", + "diary.filterExcused" to "Excusé", + "diary.filterPresent" to "Présent", + "diary.filterTest" to "Essai", + "diary.formHandedOver" to "Formulaire d'adhésion remis", + "diary.formMarkedAsHandedOver" to "Formulaire d'adhésion marqué comme remis", + "diary.freeActivities" to "Activités libres", + "diary.gallery" to "Galerie des membres", + "diary.galleryCreating" to "Création de la galerie…", + "diary.group" to "Groupe...", + "diary.groupDeletedSuccessfully" to "Le groupe a été supprimé avec succès.", + "diary.groupManagement" to "Gestion des groupes", + "diary.groupsCreated" to "{count} groupes ont été créés avec succès.", + "diary.groupsLabel" to "Groupes", + "diary.groupsSection" to "Groupes", + "diary.leader" to "Responsable", + "diary.min" to "min", + "diary.minutes" to "Minutes", + "diary.mustCreateAtLeastTwoGroups" to "Il faut créer au moins 2 groupes la première fois.", + "diary.nextAppointment" to "Prochain rendez-vous", + "diary.noActiveTrainingDay" to "Aucun jour d'entraînement sélectionné.", + "diary.noEntries" to "Aucune entrée", + "diary.noFreeActivitiesYet" to "Aucune activité libre n'a encore été enregistrée.", + "diary.noParticipants" to "Aucun participant disponible pour ce jour d'entraînement.", + "diary.numberOfGroups" to "Nombre de groupes", + "diary.oneGroupAdded" to "1 groupe a été ajouté avec succès.", + "diary.openPlanItems" to "{count} ouvert(s)", + "diary.openPlanItemsLabel" to "Statut du plan", + "diary.overallActivity" to "Activité globale", + "diary.participants" to "Participants", + "diary.participantStatusCancelled" to "Annulé", + "diary.participantStatusExcused" to "Excusé", + "diary.participantStatusNone" to "Aucun statut", + "diary.planActivitiesCount" to "Activités planifiées", + "diary.planAddHint" to "Ajoutez de nouveaux éléments du plan avec les actions ci-dessus.", + "diary.planEmptyState" to "Aucun élément n'a encore été saisi dans le plan d'entraînement.", + "diary.quickAdd" to "+ Ajout rapide", + "diary.searchParticipants" to "Rechercher des participants", + "diary.selectGroup" to "Sélectionner un groupe...", + "diary.selectGroupAndActivity" to "Veuillez sélectionner un groupe et saisir une activité.", + "diary.selectParticipantAndNote" to "Veuillez sélectionner un participant et saisir une note.", + "diary.selectTags" to "Sélectionner des tags", + "diary.selectTrainingGroup" to "Sélectionner un groupe d'entraînement", + "diary.selectTrainingGroupPlaceholder" to "Veuillez choisir...", + "diary.showImage" to "Afficher image/schéma", + "diary.skipSuggestion" to "Continuer sans suggestion", + "diary.standardActivities" to "Activités standard", + "diary.standardActivityAddError" to "L'activité standard n'a pas pu être ajoutée.", + "diary.standardDurationShort" to "min", + "diary.startTime" to "Heure de début", + "diary.statusEmpty" to "Ce jour d'entraînement est encore vide.", + "diary.statusInProgress" to "Ce jour d'entraînement est partiellement préparé.", + "diary.statusOpenShort" to "Ouvert", + "diary.statusReady" to "Les horaires et le plan d'entraînement sont définis.", + "diary.statusReadyShort" to "Prêt", + "diary.suggestion" to "Suggestion", + "diary.timeblock" to "Bloc horaire", + "diary.timeblocksCount" to "Blocs horaires", + "diary.title" to "Journal d'entraînement", + "diary.today" to "Aujourd'hui", + "diary.trainingDayAsPDF" to "Télécharger le jour d'entraînement en PDF", + "diary.trainingDayAsPDFShort" to "Jour d'entraînement en PDF", + "diary.trainingDayChecklist" to "Liste de contrôle finale", + "diary.trainingDaySection" to "Jour d'entraînement", + "diary.trainingDaySummaryPdfShort" to "Résumé des participants en PDF", + "diary.trainingEnd" to "Fin de l'entraînement", + "diary.trainingPlan" to "Plan d'entraînement", + "diary.trainingPlanAsPDF" to "Plan d'entraînement en PDF", + "diary.trainingPlanPdfShort" to "Planning en PDF", + "diary.trainingStart" to "Début de l'entraînement", + "diary.trainingTimesUpdated" to "Horaires d'entraînement mis à jour avec succès.", + "diary.trainingWindow" to "Plage d'entraînement", + "diary.trainingWindowUnset" to "Pas encore défini", + "diary.unassignedPlanItems" to "{count} ouvert(s)", + "diary.updateTimes" to "Mettre à jour les horaires", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Allemand)", + "languages.de-CH" to "Schwiizerdütsch (Allemand suisse)", + "languages.en-AU" to "English (Anglais (AU))", + "languages.en-GB" to "English (Anglais (GB))", + "languages.en-US" to "English (Anglais (US))", + "languages.es" to "Español (Espagnol)", + "languages.fil" to "Filipino", + "languages.fr" to "Français", + "languages.it" to "Italiano (Italien)", + "languages.ja" to "日本語 (Japonais)", + "languages.pl" to "Polski (Polonais)", + "languages.th" to "ไทย (Thaï)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Chinois)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Actions", + "members.active" to "Actif", + "members.activeMembers" to "Membres actifs", + "members.addEmail" to "Ajouter une adresse e-mail", + "members.addGroup" to "Ajouter un groupe...", + "members.addPhone" to "Ajouter un numéro", + "members.address" to "Adresse", + "members.adultReleaseApproved" to "Autorisé pour les adultes", + "members.adultReserveApproved" to "Réserve adulte", + "members.adults" to "Adultes (20+)", + "members.age" to "Âge", + "members.ageFromPlaceholder" to "de", + "members.ageGroup" to "Catégorie d'âge", + "members.ageRange" to "Âge de - à", + "members.ageToPlaceholder" to "à", + "members.batchFormsMarkedEmpty" to "Aucun formulaire non vérifié dans la sélection actuelle.", + "members.batchFormsMarkedSuccess" to "{count} formulaire(s) marqué(s) comme vérifié(s).", + "members.batchMarkedRegularEmpty" to "Aucun membre à l’essai dans la sélection actuelle.", + "members.batchMarkedRegularSuccess" to "{count} membre(s) à l’essai marqué(s) comme réguliers.", + "members.batchPartialFailure" to "{success} réussis, {failed} échoués.", + "members.birthdate" to "Date de naissance", + "members.bulkActions" to "Actions groupées", + "members.camera" to "Caméra", + "members.change" to "Modifier", + "members.city" to "Ville", + "members.clearFields" to "Vider les champs", + "members.clearFilters" to "Réinitialiser les filtres", + "members.clickTtRequestAction" to "Demander l'autorisation Click-TT", + "members.clickTtRequestConfirm" to "Faut-il lancer la demande Click-TT automatisée pour {name} ?", + "members.clickTtRequestError" to "La demande Click-TT n'a pas pu être envoyée.", + "members.clickTtRequestFailedLog" to "Échec de la demande Click-TT", + "members.clickTtRequestHint" to "La demande est traitée automatiquement par le workflow Click-TT du backend.", + "members.clickTtRequestPending" to "Demande Click-TT en cours", + "members.clickTtRequestPendingShort" to "En cours...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Veuillez encore vous connecter à click-tt et envoyer la demande.", + "members.clickTtRequestTitle" to "Lancer une demande Click-TT", + "members.clickTtSubmitted" to "Click-TT envoyé", + "members.closeEditor" to "Fermer l'éditeur", + "members.composeEmail" to "Préparer un e-mail", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copier le résumé des contacts", + "members.copyContactSummarySuccess" to "Résumé des contacts copié dans le presse-papiers.", + "members.copyEmails" to "Copier les e-mails", + "members.copyEmailsEmpty" to "Aucune adresse e-mail dans la sélection actuelle.", + "members.copyEmailsSuccess" to "Liste e-mail copiée dans le presse-papiers.", + "members.copyPhones" to "Copier les téléphones", + "members.copyPhonesEmpty" to "Aucun numéro de téléphone dans la sélection actuelle.", + "members.copyPhonesSuccess" to "Liste téléphonique copiée dans le presse-papiers.", + "members.create" to "Créer", + "members.createNewMember" to "Créer un nouveau membre", + "members.dataIssueAddress" to "Adresse manquante", + "members.dataIssueBirthdate" to "Date de naissance manquante", + "members.dataIssueCity" to "Ville manquante", + "members.dataIssueEmail" to "E-mail manquant", + "members.dataIssueGender" to "Sexe non défini", + "members.dataIssuePhone" to "Téléphone manquant", + "members.dataIssuePostalCode" to "Code postal manquant", + "members.dataIssueStreet" to "Rue manquante", + "members.dataIssueTrainingGroup" to "Groupe d'entraînement manquant", + "members.dataQuality" to "Qualité des données", + "members.dataQualityComplete" to "Données complètes", + "members.deactivateMember" to "Désactiver le membre", + "members.deactivateMemberConfirm" to "Voulez-vous vraiment désactiver \"{name}\" ?", + "members.deactivateMemberTitle" to "Désactiver le membre", + "members.deleteImageConfirm" to "Voulez-vous vraiment supprimer cette image ?", + "members.deleteImageTitle" to "Supprimer l'image", + "members.editHint" to "Un clic sur une ligne ouvre l’éditeur.", + "members.editMember" to "Modifier le membre", + "members.editorAssignTrainingGroupHint" to "Veuillez attribuer au moins un groupe d'entraînement.", + "members.editorCreateHint" to "Créer un nouveau membre et saisir directement ses coordonnées.", + "members.editorEditHint" to "Modifier les données de {name}.", + "members.editorRecommendedEntry" to "Entrée recommandée", + "members.emailAddress" to "Adresse e-mail", + "members.emailAddressShort" to "E-mail", + "members.emails" to "Adresses e-mail", + "members.errorAddingToGroup" to "Erreur lors de l'ajout au groupe", + "members.errorDeactivatingMember" to "Erreur lors de la désactivation du membre", + "members.errorDeletingImage" to "L'image n'a pas pu être supprimée", + "members.errorLoadingImage" to "Erreur lors du chargement de l'image", + "members.errorLoadingMembers" to "Échec du chargement de la liste des membres.", + "members.errorLoadingTrainingParticipations" to "Erreur lors du chargement des participations", + "members.errorMarkingForm" to "Erreur lors du marquage du formulaire.", + "members.errorRemovingFromGroup" to "Erreur lors du retrait du groupe", + "members.errorRemovingTestMembership" to "Erreur lors de la suppression de l'adhésion d'essai.", + "members.errorRotatingImage" to "Erreur lors de la rotation de l'image", + "members.errorSavingMember" to "Erreur lors de l'enregistrement du membre", + "members.errorSettingPrimaryImage" to "L'image principale n'a pas pu être définie", + "members.errorTransfer" to "Erreur lors du transfert", + "members.errorUpdatingRatings" to "Erreur lors de la mise à jour des valeurs TTR/QTTR", + "members.errorUploadingImage" to "L'image n'a pas pu être téléversée", + "members.exercises" to "Exercices", + "members.exportCsv" to "Exporter en CSV", + "members.exportCsvFile" to "selection-membres.csv", + "members.exportEmails" to "E-mail", + "members.exportMembersSelected" to "membres actuellement sélectionnés", + "members.exportPhones" to "Téléphone", + "members.exportPreview" to "Aperçu de l'export", + "members.exportPreviewEmpty" to "Aucun membre dans la sélection actuelle", + "members.exportPreviewNames" to "Aperçu", + "members.exportReachableByEmail" to "avec adresse e-mail", + "members.exportReachableByPhone" to "avec numéro de téléphone", + "members.firstName" to "Prénom", + "members.formHandedOver" to "Formulaire d'adhésion remis", + "members.formMarkedAsHandedOver" to "Le formulaire d'adhésion a été marqué comme remis.", + "members.gender" to "Sexe", + "members.genderDiverse" to "Divers", + "members.genderFemale" to "Féminin", + "members.genderMale" to "Masculin", + "members.genderUnknown" to "Inconnu", + "members.generatePhoneList" to "Générer la liste téléphonique", + "members.groupPhotoCrop" to "Traiter la photo de groupe", + "members.groupPhotoCropSaved" to "Photo du membre enregistrée depuis la photo de groupe.", + "members.image" to "Image", + "members.imageDeleted" to "L'image a été supprimée.", + "members.imageInternet" to "Image (web ?)", + "members.imagePreview" to "Aperçu de l'image du membre", + "members.imageUpdated" to "Image mise à jour", + "members.inactive" to "inactif", + "members.inactiveMembers" to "Membres inactifs", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "U17 (17 ans et moins)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Nom", + "members.lastTrainingFilter" to "Dernier entraînement", + "members.lastTrainingFilterHasDate" to "Avec date enregistrée", + "members.lastTrainingFilterHint" to "Tableau : saison actuelle dans la colonne AK. Détail complet (deux saisons, dernier entraînement, participations) au survol de la ligne.", + "members.lastTrainingFilterNoDate" to "Sans dernier entraînement", + "members.lastTrainingFilterNotInTraining" to "« Plus à l’entraînement »", + "members.loadingMembers" to "Chargement des membres...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Marquer le formulaire comme vérifié", + "members.markFormsForSelection" to "Vérifier les formulaires de la sélection", + "members.markRegular" to "Marquer comme régulier", + "members.markRegularForSelection" to "Marquer la sélection comme régulière", + "members.memberDeactivated" to "Membre désactivé", + "members.memberDetails" to "Détails du membre", + "members.memberFormHandedOver" to "Formulaire d'adhésion remis", + "members.memberImage" to "Photo du membre", + "members.memberImages" to "Images du membre", + "members.memberInfo" to "Informations membre", + "members.memberScope" to "Périmètre des membres", + "members.missingTtrHistoryId" to "Aucun identifiant myTischtennis enregistré", + "members.name" to "Nom, prénom", + "members.newMember" to "Nouveau membre", + "members.noAddressShort" to "Pas d’adresse", + "members.noEmailShort" to "Pas d’e-mail", + "members.noGroupsAssigned" to "Aucun groupe attribué", + "members.noGroupsAvailable" to "Aucun groupe disponible", + "members.noOpenTasks" to "Aucune tâche ouverte", + "members.noPhoneShort" to "Pas de téléphone", + "members.notes" to "Notes", + "members.noTestMembership" to "N'est plus membre à l'essai", + "members.notInTrainingTooltip" to "Aucune participation depuis {weeks} semaines d’entraînement", + "members.onlyActiveMembers" to "Seuls les membres actifs sont inclus", + "members.openTasks" to "Tâches ouvertes", + "members.parent" to "Parent", + "members.parentFallback" to "Parent", + "members.parentName" to "Nom (p. ex. mère, père)", + "members.phoneList" to "liste-telephonique.pdf", + "members.phoneListForSelection" to "Liste téléphonique pour la sélection", + "members.phoneListSelectionFile" to "liste-telephonique-selection.pdf", + "members.phoneNumber" to "Numéro de téléphone", + "members.phoneNumberShort" to "Tél.", + "members.phones" to "Numéros de téléphone", + "members.picsInInternetAllowed" to "Photos autorisées en ligne", + "members.postalCode" to "Code postal", + "members.previewLastTraining" to "Dernier entraînement", + "members.previewNoLastTraining" to "Aucune participation pour le moment", + "members.primary" to "Principal", + "members.primaryImageUpdated" to "Image principale mise à jour.", + "members.remove" to "Retirer", + "members.resultsVisible" to "membres visibles", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Actifs", + "members.scopeActiveDataIncomplete" to "Actif + données incomplètes", + "members.scopeActiveTest" to "Actif + essai", + "members.scopeAll" to "Tous", + "members.scopeDataIncomplete" to "Données incomplètes", + "members.scopeInactive" to "Inactifs", + "members.scopeNeedsForm" to "Formulaire non vérifié", + "members.scopeNotTraining" to "Ne s'entraîne plus", + "members.scopeTest" to "Essai", + "members.search" to "Rechercher", + "members.searchAndFilter" to "Recherche et filtres", + "members.searchPlaceholder" to "Rechercher par nom, ville, téléphone ou e-mail", + "members.selectFile" to "Choisir un fichier", + "members.showInactiveMembers" to "Afficher les membres inactifs", + "members.showTrainingParticipationsColumn" to "Afficher la colonne « Participations »", + "members.showTtrHistory" to "Afficher l’historique TTR", + "members.sixOrMoreParticipations" to "6 participations ou plus à l'entraînement", + "members.sortAge" to "Âge", + "members.sortBirthday" to "Date de naissance", + "members.sortBy" to "Trier par", + "members.sortFirstName" to "Prénom", + "members.sortLastName" to "Nom", + "members.sortLastTraining" to "Dernier entraînement", + "members.sortOpenTasks" to "Tâches ouvertes", + "members.sortQttr" to "Valeur QTTR", + "members.status" to "Statut", + "members.street" to "Rue", + "members.subtitle" to "Rechercher, filtrer et modifier les membres directement.", + "members.taskActionMarkRegular" to "Marquer régulier", + "members.taskActionRequest" to "Demander", + "members.taskActionReview" to "Ouvrir", + "members.taskActionVerify" to "Vérifier", + "members.taskAssignTrainingGroup" to "Attribuer un groupe d'entraînement", + "members.taskCheckClickTt" to "Vérifier l'autorisation Click-TT", + "members.taskCheckDataQuality" to "Vérifier la qualité des données", + "members.taskCheckTrainingStatus" to "Vérifier le statut d'entraînement", + "members.taskReviewTrialStatus" to "Vérifier le statut d'essai", + "members.taskVerifyForm" to "Vérifier le formulaire", + "members.testMember" to "Essai", + "members.testMembers" to "Membres à l'essai", + "members.testMembership" to "Adhésion d'essai", + "members.testMembershipRemoved" to "L'adhésion d'essai a été supprimée.", + "members.threeOrMoreParticipations" to "3 participations ou plus à l'entraînement", + "members.title" to "Membres", + "members.toggleSortDirection" to "Changer le sens du tri", + "members.trainingGroups" to "Groupes d'entraînement", + "members.trainingParticipations" to "Participations à l'entraînement", + "members.transferErrorTitle" to "Erreur de transfert", + "members.transferMembers" to "Transférer des membres", + "members.transferSuccessTitle" to "Transfert réussi", + "members.ttAdult" to "Adultes (pas jeunes selon la date limite)", + "members.ttAgeClassCol" to "Classe d'âge (TT)", + "members.ttFilterGroupJ" to "Filles et garçons (mixte)", + "members.ttFilterGroupM" to "Filles uniquement", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "actuelle", + "members.ttSeasonFilter" to "Saison (date limite)", + "members.ttSeasonNextTag" to "à venir", + "members.ttStichtagHint" to "Date limite le 1er janv. (DTTB). Garçons : classes J uniquement. Filles : J et M.", + "members.updateRatings" to "Mettre à jour TTR/QTTR depuis myTischtennis", + "members.updating" to "Mise à jour...", + "members.visibleMembers" to "Membres visibles", + "members.wantsToPlay" to "Veut jouer", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Annuler", + "messages.confirm" to "Confirmer", + "messages.error" to "Erreur", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Information", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Succès", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Avertissement", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Approbations", + "navigation.backToHome" to "Retour à l'accueil", + "navigation.billing" to "Facturation", + "navigation.clickTtAccount" to "Compte HTTV / click-TT", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Paramètres du club", + "navigation.clubTournaments" to "Tournois du club", + "navigation.competitions" to "Compétitions", + "navigation.dailyBusiness" to "Gestion quotidienne", + "navigation.diary" to "Journal", + "navigation.home" to "Accueil", + "navigation.login" to "Connexion", + "navigation.logout" to "Déconnexion", + "navigation.logs" to "Journaux système", + "navigation.members" to "Membres", + "navigation.memberTransfer" to "Transfert de membres", + "navigation.myTischtennisAccount" to "Compte myTischtennis", + "navigation.orders" to "Commandes", + "navigation.permissions" to "Autorisations", + "navigation.personalSettings" to "Paramètres personnels", + "navigation.predefinedActivities" to "Activités prédéfinies", + "navigation.register" to "S'inscrire", + "navigation.schedule" to "Calendriers", + "navigation.settings" to "Paramètres", + "navigation.statistics" to "Statistiques d'entraînement", + "navigation.teamManagement" to "Gestion d'équipe", + "navigation.tournamentParticipations" to "Participations aux tournois", + "navigation.tournaments" to "Tournois", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Créer une commande", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Coût", + "orders.dateAutoHint" to "La date est définie automatiquement et chaque modification est enregistrée avec une date.", + "orders.errorLoading" to "Impossible de charger les commandes.", + "orders.errorSaving" to "Impossible d'enregistrer la commande.", + "orders.filterAllClubs" to "Tous les clubs", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Tous les statuts", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Toutes les commandes de tous les clubs peuvent être consultées et gérées ici.", + "orders.globalTitle" to "Commandes de tous les clubs", + "orders.history" to "Historique", + "orders.item" to "Article", + "orders.itemPlaceholder" to "p. ex. maillot, hoodie ou housse de raquette", + "orders.loading" to "Chargement des commandes...", + "orders.member" to "Membre", + "orders.memberTitle" to "Commandes : {name}", + "orders.noOrdersGlobal" to "Il n'y a actuellement aucune commande.", + "orders.noOrdersMember" to "Aucune commande n'existe encore pour ce membre.", + "orders.open" to "Reste à payer", + "orders.orderDate" to "Créé le", + "orders.paid" to "Payé", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Rechercher un club, un membre ou un article", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Statut", + "orders.statusArrived" to "article arrivé", + "orders.statusDate" to "Dernière modification", + "orders.statusHandedOver" to "article remis", + "orders.statusOrdered" to "commandé", + "orders.statusRequested" to "souhaité", + "orders.title" to "Commandes", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club :", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Créé le :", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Ligue :", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Inscription pour :", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Ligue prévue :", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Saison :", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Catégorie d’âge d’équipe :", + "pdfGenerator.teamGenderLabel" to "Genre d’équipe :", + "pdfGenerator.teamLineupTitle" to "Composition de l’équipe", + "pdfGenerator.teamNameLabel" to "Équipe :", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Langue", + "settings.languageChanged" to "Langue modifiée avec succès", + "settings.languageDescription" to "Choisissez votre langue préférée pour l'application", + "settings.personalSettings" to "Paramètres personnels", + "settings.selectLanguage" to "Sélectionner la langue", + "settings.title" to "Paramètres", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Joueurs disponibles", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Éligibilité", + "teamManagement.eligibilityAdultRelease" to "Autorisé chez les adultes", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Autorisé + remplaçant chez les adultes", + "teamManagement.eligibilityAdultReserve" to "Remplaçant chez les adultes", + "teamManagement.eligibilityRegular" to "Régulier", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Exporter la composition en PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Phase aller", + "teamManagement.firstHalfFull" to "Phase aller (juillet - décembre)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Veuillez sélectionner une classe de ligue et enregistrer.", + "teamManagement.lineupEmpty" to "Aucun joueur n’a encore été inscrit pour cette équipe.", + "teamManagement.lineupPdfEmpty" to "Aucun joueur dans la composition – impossible de créer le PDF.", + "teamManagement.lineupPdfFilePrefix" to "Composition", + "teamManagement.lineupProposal" to "Composition selon le QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "La composition de l’équipe n’a pas pu être enregistrée.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} a plus de 30 points QTTR d’avance sur {lower}. Veuillez corriger cet ordre.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Informations manquantes :", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Collez et analysez l’URL MyTischtennis pour lier l’ID d’équipe et les données de ligue.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Ligue prévue", + "teamManagement.plannedLeagueHint" to "Facultatif. Indépendant de la ligue enregistrée (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "ex. ligue de district, après la prochaine inscription …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Phase retour", + "teamManagement.secondHalfFull" to "Phase retour (à partir du 1er janvier)", + "teamManagement.selectedLineup" to "Joueurs inscrits", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Ajouter une règle de groupes", + "tournaments.address" to "Adresse", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Toutes les données des participants sont complètes.", + "tournaments.allDataCompleteTop3" to "Toutes les données des 3 premiers sont complètes.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Veuillez ajouter au moins une règle de groupes pour {label} (p. ex. places 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Corriger", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Créer le tour final depuis le tour intermédiaire", + "tournaments.createFinalFromPreliminary" to "Créer le tour final depuis le tour préliminaire", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Créer le tour intermédiaire depuis le tour préliminaire", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Pas encore enregistré", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Voulez-vous vraiment supprimer la classe \"{name}\" ?", + "tournaments.deleteClassParticipantsDetached" to "Tous les participants seront dissociés de cette classe.", + "tournaments.deleteClassTitle" to "Supprimer la classe", + "tournaments.deleteExistingPairingsConfirm" to "Les paires existantes seront supprimées. Continuer ?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Distribuer les tables libres", + "tournaments.distributeTablesResult" to "Distribution des tables", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Convient pour : {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Veuillez saisir un nom de classe.", + "tournaments.enterExternalParticipantName" to "Veuillez saisir au moins le prénom et le nom.", + "tournaments.enterMiniLocation" to "Veuillez saisir un lieu.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Erreur lors de la répartition des tables.", + "tournaments.errorGeneratingPdf" to "Erreur lors de la génération du PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Tour final", + "tournaments.finishMatch" to "Terminer", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Génération du PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Groupes", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Tour intermédiaire (tour 2)", + "tournaments.internalStatsAbsoluteRank" to "Classement du total", + "tournaments.internalStatsAgeFilter" to "Âge et genre (simple)", + "tournaments.internalStatsAgeFilterAll" to "Toutes les catégories", + "tournaments.internalStatsAgeFilterNone" to "Aucune catégorie sélectionnée", + "tournaments.internalStatsAgeNoClass" to "Sans classe assignée", + "tournaments.internalStatsAgeSelectAll" to "Tout", + "tournaments.internalStatsAgeSelectNone" to "Aucune", + "tournaments.internalStatsAverageRank" to "Classement de la moyenne (par tournoi)", + "tournaments.internalStatsAvgPoints" to "Moy.", + "tournaments.internalStatsEmpty" to "Aucune donnée pour la période choisie.", + "tournaments.internalStatsExportPdf" to "Exporter en PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "12 derniers mois", + "tournaments.internalStatsLast3Months" to "3 derniers mois", + "tournaments.internalStatsLast6Months" to "6 derniers mois", + "tournaments.internalStatsOpenButton" to "Statistiques (simple)", + "tournaments.internalStatsPeriod" to "Période", + "tournaments.internalStatsPoints" to "Total", + "tournaments.internalStatsPointsExplain" to "Barème : dans chaque poule, le classement est exprimé en pourcentage (avec N classés : 1er = 100 %, dernier = 0 %, linéaire entre les deux ; ex æquo = même valeur). N compte tous les classés de la poule (invités inclus). Un seul joueur : 100 %. En KO : meilleur score de poule de la catégorie + 1, puis +1 par match KO gagné. Membres du club en simple uniquement.", + "tournaments.internalStatsTitle" to "Statistiques des tournois internes (simple)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} tournoi(s) sur la période (hors mini-championnats).", + "tournaments.internalStatsTtAdult" to "Adultes", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Marquer en direct", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Au moins 2 participants sont nécessaires pour créer des paires.", + "tournaments.missingDataPDF" to "Données manquantes en PDF", + "tournaments.missingDataPDFSubtitle" to "Veuillez collecter les données manquantes (marquées ____) auprès des participants et les noter ici.", + "tournaments.missingDataPDFSubtitleTop3" to "Veuillez collecter les données manquantes des 3 premiers (marquées ____) et les noter ici.", + "tournaments.missingDataPDFTitle" to "Données manquantes des participants – Mini-championnat", + "tournaments.missingDataPDFTitleTop3" to "Données manquantes – Top 3 Mini-championnat", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Nouveau set, p. ex. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Aucun match disponible où les deux joueurs sont libres.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "Aucune catégorie appropriée trouvée.", + "tournaments.noFreeTables" to "Aucune table libre disponible.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Aucun match orphelin trouvé.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Aucune règle pour le moment. Exemple : places 1 et 2 -> groupes supérieurs du tour 2.", + "tournaments.noPoolRulesYetFinal" to "Aucune règle pour le moment. Exemple : places 1 et 2 -> tour final.", + "tournaments.noTop3Yet" to "Les 3 premiers ne sont pas encore déterminés.", + "tournaments.noTournamentDate" to "Aucune date de tournoi disponible.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Aucune séance d'entraînement trouvée pour le {date}.", + "tournaments.noTrainingParticipants" to "Aucun participant n'a été trouvé dans la séance d'entraînement pour cette date.", + "tournaments.noValidTrainingParticipants" to "Aucun participant valide n'a été trouvé dans la séance d'entraînement pour cette date.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Nombre de tables", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} matchs orphelins supprimés.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Page", + "tournaments.pairingAlreadyExists" to "Cette paire existe déjà.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} paires créées, {errorCount} erreurs.", + "tournaments.participantConflicts" to "Conflits", + "tournaments.participantNotFound" to "Participant introuvable.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Téléphone", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Places de chaque groupe (p. ex. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Joueur 1", + "tournaments.playerTwo" to "Joueur 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Jouer la troisième place", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Qualification : tour préliminaire → tour intermédiaire (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Qualification : tour {from} → tour {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Classement", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Nombre de groupes", + "tournaments.roundMode" to "Mode", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Enregistrer les tours", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Veuillez d'abord sélectionner un tournoi.", + "tournaments.selectTwoDifferentPlayers" to "Veuillez sélectionner deux joueurs différents.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Réponse serveur invalide.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "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.", + "tournaments.stageConfigLoadError" to "Erreur lors du chargement de la configuration des tours.", + "tournaments.stageConfigLoading" to "Chargement de la configuration des tours…", + "tournaments.stageConfigMissingIds" to "Impossible d'enregistrer : l'identifiant du club ou du tournoi est manquant.", + "tournaments.stageConfigTitle" to "Tour intermédiaire et finale", + "tournaments.stageCreated" to "Le tour {round} a été créé.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Terminé", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "En direct", + "tournaments.statusOpen" to "Ouvert", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Table", + "tournaments.tablesDistributed" to "Les tables ont été distribuées.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Cible", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Nombre de groupes cibles", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Féminin", + "tournaments.tournamentClassGenderOpen" to "Tous", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Retirer le marquage en direct", + "tournaments.unpairedDoubles" to "Double sans partenaire", + "tournaments.useIntermediateStage" to "Utiliser un tour intermédiaire", + "tournaments.warningBirthDateMissing" to "La date de naissance manque pour cette catégorie", + "tournaments.warningClassMissing" to "Catégorie introuvable", + "tournaments.warningGenderMismatch" to "Le sexe ne correspond pas à la catégorie", + "tournaments.warningGenderMissing" to "Le sexe manque pour cette catégorie", + "tournaments.warningMissingPairing" to "Partenaire de double manquant", + "tournaments.warningTooOldForClass" to "Trop âgé pour cette catégorie", + "tournaments.warningTooYoungForClass" to "Trop jeune pour cette catégorie", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Actions", + "trainingStats.activeMembers" to "Membres actifs", + "trainingStats.allTrainingDays" to "Tous les jours d'entraînement", + "trainingStats.attendingMembers" to "Membres présents", + "trainingStats.averageParticipationCurrentMonth" to "Participation moyenne (mois en cours)", + "trainingStats.averageParticipationHalfYear" to "Participation moyenne (semestre)", + "trainingStats.averageParticipationLastMonth" to "Participation moyenne (mois précédent)", + "trainingStats.averageParticipationQuarter" to "Participation moyenne (trimestre)", + "trainingStats.averageParticipationYear" to "Participation moyenne (année)", + "trainingStats.birthdate" to "Date de naissance", + "trainingStats.date" to "Date", + "trainingStats.lastTraining" to "Dernier entraînement", + "trainingStats.memberParticipations" to "Participations des membres", + "trainingStats.name" to "Nom", + "trainingStats.noParticipants" to "Aucun participant", + "trainingStats.participants" to "Participants", + "trainingStats.participations12Months" to "Participations (12 mois)", + "trainingStats.participations3Months" to "Participations (3 mois)", + "trainingStats.participationsTotal" to "Participations (total)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Afficher les détails", + "trainingStats.title" to "Statistiques d'entraînement", + "trainingStats.trainingDayFilter" to "Jour d'entraînement", + "trainingStats.trainingDays" to "Jours d'entraînement (12 derniers mois)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Jour de semaine", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val it: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Diario di allenamento", + "app.title" to "Diario di allenamento", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Attivazione fallita. Controlla il link.", + "auth.confirmPassword" to "Conferma password", + "auth.email" to "Email", + "auth.forgotPassword" to "Password dimenticata?", + "auth.forgotPasswordDescription" to "Inserisci il tuo indirizzo email. Riceverai un link per reimpostare la password.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Accedi", + "auth.loginFailed" to "Accesso fallito. Controlla le credenziali.", + "auth.loginSuccess" to "Accesso effettuato con successo", + "auth.logout" to "Esci", + "auth.logoutSuccess" to "Uscita effettuata con successo", + "auth.newPassword" to "Nuova password", + "auth.noAccount" to "Non hai un account?", + "auth.password" to "Password", + "auth.passwordResetSuccess" to "La password è stata cambiata con successo. Ora puoi accedere.", + "auth.passwordsDoNotMatch" to "Le password non corrispondono.", + "auth.passwordTooShort" to "La password deve contenere almeno 6 caratteri.", + "auth.register" to "Registrati", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Ricordami", + "auth.resetEmailSent" to "Se esiste un account con questa email, è stato inviato un link di reimpostazione. Controlla la posta (e lo spam).", + "auth.resetFailed" to "La password non è stata modificata. Il link potrebbe essere scaduto.", + "auth.resetPassword" to "Imposta nuova password", + "auth.resetRequestFailed" to "Richiesta fallita. Riprova.", + "auth.saveNewPassword" to "Salva password", + "auth.saving" to "Salvataggio...", + "auth.sending" to "Invio in corso...", + "auth.sendResetLink" to "Invia link", + "auth.sessionExpired" to "La tua sessione è scaduta. Verrai disconnesso.", + "auth.toLogin" to "Vai al login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Accesso al club negato.", + "club.accessRequested" to "Accesso richiesto.", + "club.accessRequestFailed" to "Impossibile inviare la richiesta di accesso.", + "club.accessRequestPending" to "L’accesso a questo club è stato richiesto. Attendere prego.", + "club.create" to "Crea club", + "club.createTitle" to "Crea club", + "club.diary" to "Diario di allenamento", + "club.errorLoadingRequests" to "Errore durante il caricamento delle richieste aperte", + "club.load" to "Carica", + "club.members" to "Membri", + "club.mobileSelectHint" to "Seleziona prima un club per usare l'app sullo smartphone.", + "club.name" to "Nome del club", + "club.new" to "Nuovo club", + "club.noAccess" to "Non hai ancora accesso a questo club.", + "club.openAccessRequests" to "Richieste di accesso aperte", + "club.openRequests" to "Richieste di accesso aperte", + "club.requestAccess" to "Richiedi accesso", + "club.select" to "Seleziona club", + "club.selectPlaceholder" to "Scegli un club...", + "club.title" to "Club", + "club.trainingDiary" to "Diario di allenamento", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Azioni", + "common.active" to "Attivo", + "common.add" to "Aggiungi", + "common.all" to "Tutti", + "common.apply" to "Applica", + "common.back" to "Indietro", + "common.cancel" to "Annulla", + "common.choose" to "Scegli", + "common.clear" to "Cancella", + "common.close" to "Chiudi", + "common.confirm" to "Conferma", + "common.create" to "Crea", + "common.date" to "Data", + "common.days" to "giorni", + "common.delete" to "Elimina", + "common.description" to "Descrizione", + "common.details" to "Dettagli", + "common.disabled" to "Disabilitato", + "common.download" to "Scarica", + "common.edit" to "Modifica", + "common.enabled" to "Abilitato", + "common.filter" to "Filtra", + "common.hours" to "ore", + "common.in" to "in", + "common.inactive" to "Inattivo", + "common.loading" to "Caricamento...", + "common.min" to "min", + "common.minutes" to "minuti", + "common.months" to "mesi", + "common.move" to "Sposta", + "common.name" to "Nome", + "common.new" to "Nuovo", + "common.next" to "Avanti", + "common.no" to "No", + "common.ok" to "OK", + "common.optional" to "Opzionale", + "common.period" to "Periodo", + "common.previous" to "Precedente", + "common.refresh" to "Ricarica", + "common.remove" to "Rimuovi", + "common.required" to "Obbligatorio", + "common.reset" to "Reimposta", + "common.save" to "Salva", + "common.saved" to "Salvato", + "common.saving" to "Salvataggio...", + "common.search" to "Cerca", + "common.select" to "Seleziona", + "common.status" to "Stato", + "common.submit" to "Invia", + "common.time" to "Ora", + "common.today" to "Oggi", + "common.type" to "Tipo", + "common.update" to "Aggiorna", + "common.view" to "Visualizza", + "common.weeks" to "settimane", + "common.years" to "anni", + "common.yes" to "Sì", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Aggiungi colpo", + "courtDrawingTool.backhand" to "Rovescio", + "courtDrawingTool.codeLabel" to "Codice", + "courtDrawingTool.completeFirstStroke" to "Completa il primo colpo", + "courtDrawingTool.completeFirstStrokeHint" to "Scegli posizione iniziale, lato, effetto e obiettivo. Lo schema apparirà poi.", + "courtDrawingTool.configureExercise" to "Configura esercizio", + "courtDrawingTool.counterSpin" to "Controeffetto", + "courtDrawingTool.forehand" to "Dritto", + "courtDrawingTool.noAdditionalStrokes" to "Nessun colpo successivo aggiunto finora.", + "courtDrawingTool.preview" to "Anteprima", + "courtDrawingTool.previewHint" to "Lo schema appare non appena il primo colpo è configurato completamente.", + "courtDrawingTool.service" to "Servizio:", + "courtDrawingTool.serviceTitle" to "Servizio", + "courtDrawingTool.sidespin" to "Effetto laterale", + "courtDrawingTool.sideUnderspin" to "Laterale tagliato", + "courtDrawingTool.spin" to "Effetto:", + "courtDrawingTool.startLeft" to "sinistra", + "courtDrawingTool.startMiddle" to "centro", + "courtDrawingTool.startRight" to "destra", + "courtDrawingTool.stepAdditionalStrokes" to "4. Colpi successivi", + "courtDrawingTool.stepAdditionalStrokesHint" to "Aggiungi facoltativamente altre palle come sequenza.", + "courtDrawingTool.stepFirstStroke" to "2. Primo colpo", + "courtDrawingTool.stepFirstStrokeHint" to "Definisci il lato e l’effetto della prima palla.", + "courtDrawingTool.stepStartPosition" to "1. Posizione iniziale", + "courtDrawingTool.stepStartPositionHint" to "Da quale posizione di servizio inizia l’esercizio?", + "courtDrawingTool.stepTarget" to "3. Obiettivo", + "courtDrawingTool.stepTargetHint" to "Scegli la zona bersaglio per il primo colpo.", + "courtDrawingTool.strokeSide" to "Lato", + "courtDrawingTool.strokeTypeBlock" to "Blocco", + "courtDrawingTool.strokeTypeChopDefense" to "Difesa tagliata", + "courtDrawingTool.strokeTypeCounter" to "Contro", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Tipo di colpo", + "courtDrawingTool.strokeTypeLobDefense" to "Difesa alta", + "courtDrawingTool.strokeTypePush" to "Palleggio corto", + "courtDrawingTool.strokeTypeSmash" to "Schiacciata", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Rovescio mezzo-lungo", + "courtDrawingTool.targetBackhandLong" to "Rovescio lungo", + "courtDrawingTool.targetBackhandShort" to "Rovescio corto", + "courtDrawingTool.targetForehandHalfLong" to "Dritto mezzo-lungo", + "courtDrawingTool.targetForehandLong" to "Dritto lungo", + "courtDrawingTool.targetForehandShort" to "Dritto corto", + "courtDrawingTool.targetMiddleHalfLong" to "Centro mezzo-lungo", + "courtDrawingTool.targetMiddleLong" to "Centro lungo", + "courtDrawingTool.targetMiddleShort" to "Centro corto", + "courtDrawingTool.targetPosition" to "Posizione bersaglio:", + "courtDrawingTool.targetPositionLabel" to "Posizione bersaglio", + "courtDrawingTool.title" to "Schema di esercizio di tennistavolo", + "courtDrawingTool.titleLabel" to "Titolo", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "verso", + "courtDrawingTool.underspin" to "Taglio", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Giorno di allenamento attivo", + "diary.activities" to "Attività", + "diary.activity" to "Attività", + "diary.activityDrawing" to "Disegno attività", + "diary.activityImage" to "Immagine attività", + "diary.activityNotFound" to "Attività non trovata. Scegline una dalla lista.", + "diary.activityOrTimeblock" to "Attività / blocco orario", + "diary.activityPlaceholder" to "Attività", + "diary.activityRequired" to "Inserisci un’attività.", + "diary.addActivity" to "Aggiungi attività", + "diary.addGroup" to "Aggiungi gruppo", + "diary.addGroupActivity" to "Aggiungi attività di gruppo", + "diary.addGroupButton" to "+ Gruppo", + "diary.addTimeblock" to "Blocco orario", + "diary.all" to "Tutti", + "diary.applySuggestion" to "Applica suggerimento", + "diary.assignParticipants" to "Assegna partecipanti", + "diary.assignParticipantsForGroupActivity" to "Assegna partecipanti all’attività di gruppo", + "diary.assignShort" to "Assegna", + "diary.bookAccident" to "Registra incidente", + "diary.confirmDelete" to "Conferma eliminazione", + "diary.confirmDeleteDate" to "Vuoi davvero eliminare questa data?", + "diary.confirmDeleteDateDetails" to "Verranno eliminati anche tutti i dati associati.", + "diary.confirmDeleteGroup" to "Vuoi davvero eliminare il gruppo \"{name}\"?", + "diary.createDate" to "Crea data", + "diary.createDrawing" to "Crea disegno esercizio", + "diary.createGroups" to "Crea gruppi", + "diary.createNew" to "Crea", + "diary.createNewDate" to "Crea nuova data", + "diary.date" to "Data", + "diary.dateCannotBeDeleted" to "La data non può essere eliminata", + "diary.dateCannotBeDeletedDetails" to "Sono ancora presenti contenuti (piano, partecipanti, attività, incidenti o note).", + "diary.dateNoLongerCurrent" to "La data selezionata non era più attuale. Riprova.", + "diary.delete" to "Elimina", + "diary.deleteDate" to "Elimina data", + "diary.deleteGroup" to "Elimina gruppo", + "diary.duration" to "Durata", + "diary.durationExampleLong" to "es. 2x7 o 3*5", + "diary.durationExampleShort" to "es. 2x7", + "diary.durationMinutes" to "Durata (min)", + "diary.editActivity" to "Modifica attività", + "diary.editGroupActivity" to "Modifica attività di gruppo", + "diary.editTrainingTimes" to "Modifica orari di allenamento", + "diary.errorCreatingActivity" to "Errore durante la creazione dell’attività", + "diary.errorCreatingGroups" to "Errore durante la creazione dei gruppi", + "diary.errorDeletingGroup" to "Errore durante l’eliminazione del gruppo", + "diary.errorLoadingPredefinedActivities" to "Errore durante il caricamento delle attività predefinite", + "diary.errorMarkingForm" to "Errore durante la marcatura del modulo", + "diary.errorOccurred" to "Si è verificato un errore. Riprova.", + "diary.existingGroups" to "Gruppi esistenti", + "diary.filterAbsent" to "Assente", + "diary.filterAll" to "Tutti", + "diary.filterExcused" to "Giustificato", + "diary.filterPresent" to "Presente", + "diary.filterTest" to "Prova", + "diary.formHandedOver" to "Modulo di iscrizione consegnato", + "diary.formMarkedAsHandedOver" to "Modulo di iscrizione contrassegnato come consegnato", + "diary.freeActivities" to "Attività libere", + "diary.gallery" to "Galleria membri", + "diary.galleryCreating" to "Creazione galleria…", + "diary.group" to "Gruppo...", + "diary.groupDeletedSuccessfully" to "Gruppo eliminato correttamente.", + "diary.groupManagement" to "Gestione gruppi", + "diary.groupsCreated" to "{count} gruppi creati correttamente.", + "diary.groupsLabel" to "Gruppi", + "diary.groupsSection" to "Gruppi", + "diary.leader" to "Responsabile", + "diary.min" to "min", + "diary.minutes" to "Minuti", + "diary.mustCreateAtLeastTwoGroups" to "La prima volta è necessario creare almeno 2 gruppi.", + "diary.nextAppointment" to "Prossimo appuntamento", + "diary.noActiveTrainingDay" to "Nessun giorno di allenamento selezionato.", + "diary.noEntries" to "Nessuna voce", + "diary.noFreeActivitiesYet" to "Non sono ancora state registrate attività libere.", + "diary.noParticipants" to "Nessun partecipante disponibile per questo giorno di allenamento.", + "diary.numberOfGroups" to "Numero di gruppi", + "diary.oneGroupAdded" to "1 gruppo aggiunto correttamente.", + "diary.openPlanItems" to "{count} aperti", + "diary.openPlanItemsLabel" to "Stato del piano", + "diary.overallActivity" to "Attività complessiva", + "diary.participants" to "Partecipanti", + "diary.participantStatusCancelled" to "Annullato", + "diary.participantStatusExcused" to "Giustificato", + "diary.participantStatusNone" to "Nessuno stato", + "diary.planActivitiesCount" to "Attività pianificate", + "diary.planAddHint" to "Aggiungi nuovi elementi del piano usando le azioni sopra.", + "diary.planEmptyState" to "Non è ancora stato inserito nulla nel piano di allenamento.", + "diary.quickAdd" to "+ Aggiunta rapida", + "diary.searchParticipants" to "Cerca partecipanti", + "diary.selectGroup" to "Seleziona gruppo...", + "diary.selectGroupAndActivity" to "Seleziona un gruppo e inserisci un’attività.", + "diary.selectParticipantAndNote" to "Seleziona un partecipante e inserisci una nota.", + "diary.selectTags" to "Seleziona tag", + "diary.selectTrainingGroup" to "Seleziona gruppo di allenamento", + "diary.selectTrainingGroupPlaceholder" to "Seleziona...", + "diary.showImage" to "Mostra immagine/disegno", + "diary.skipSuggestion" to "Continua senza suggerimento", + "diary.standardActivities" to "Attività standard", + "diary.standardActivityAddError" to "Non è stato possibile aggiungere l'attività standard.", + "diary.standardDurationShort" to "min", + "diary.startTime" to "Ora di inizio", + "diary.statusEmpty" to "Questo giorno di allenamento è ancora vuoto.", + "diary.statusInProgress" to "Questo giorno di allenamento è preparato solo in parte.", + "diary.statusOpenShort" to "Aperto", + "diary.statusReady" to "Orari e piano di allenamento sono impostati.", + "diary.statusReadyShort" to "Pronto", + "diary.suggestion" to "Suggerimento", + "diary.timeblock" to "Blocco orario", + "diary.timeblocksCount" to "Blocchi orari", + "diary.title" to "Diario di allenamento", + "diary.today" to "Oggi", + "diary.trainingDayAsPDF" to "Scarica il giorno di allenamento in PDF", + "diary.trainingDayAsPDFShort" to "Giorno di allenamento in PDF", + "diary.trainingDayChecklist" to "Checklist finale", + "diary.trainingDaySection" to "Giorno di allenamento", + "diary.trainingDaySummaryPdfShort" to "Riepilogo partecipanti in PDF", + "diary.trainingEnd" to "Fine allenamento", + "diary.trainingPlan" to "Piano di allenamento", + "diary.trainingPlanAsPDF" to "Piano di allenamento in PDF", + "diary.trainingPlanPdfShort" to "Programma in PDF", + "diary.trainingStart" to "Inizio allenamento", + "diary.trainingTimesUpdated" to "Orari di allenamento aggiornati correttamente.", + "diary.trainingWindow" to "Fascia di allenamento", + "diary.trainingWindowUnset" to "Non ancora impostato", + "diary.unassignedPlanItems" to "{count} aperti", + "diary.updateTimes" to "Aggiorna orari", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Tedesco)", + "languages.de-CH" to "Schwiizerdütsch (Tedesco svizzero)", + "languages.en-AU" to "English (Inglese (AU))", + "languages.en-GB" to "English (Inglese (GB))", + "languages.en-US" to "English (Inglese (US))", + "languages.es" to "Español (Spagnolo)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (Francese)", + "languages.it" to "Italiano", + "languages.ja" to "日本語 (Giapponese)", + "languages.pl" to "Polski (Polacco)", + "languages.th" to "ไทย (Tailandese)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Cinese)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Azioni", + "members.active" to "Attivo", + "members.activeMembers" to "Membri attivi", + "members.addEmail" to "Aggiungi indirizzo e-mail", + "members.addGroup" to "Aggiungi gruppo...", + "members.addPhone" to "Aggiungi numero di telefono", + "members.address" to "Indirizzo", + "members.adultReleaseApproved" to "Approvato per adulti", + "members.adultReserveApproved" to "Riserva adulti", + "members.adults" to "Adulti (20+)", + "members.age" to "Età", + "members.ageFromPlaceholder" to "da", + "members.ageGroup" to "Fascia d’età", + "members.ageRange" to "Età da - a", + "members.ageToPlaceholder" to "a", + "members.batchFormsMarkedEmpty" to "Nessun modulo non verificato nella selezione attuale.", + "members.batchFormsMarkedSuccess" to "{count} modulo/i contrassegnato/i come verificato/i.", + "members.batchMarkedRegularEmpty" to "Nessun membro in prova nella selezione attuale.", + "members.batchMarkedRegularSuccess" to "{count} membro/i in prova contrassegnato/i come regolare/i.", + "members.batchPartialFailure" to "{success} riusciti, {failed} falliti.", + "members.birthdate" to "Data di nascita", + "members.bulkActions" to "Azioni di gruppo", + "members.camera" to "Fotocamera", + "members.change" to "Modifica", + "members.city" to "Città", + "members.clearFields" to "Svuota campi", + "members.clearFilters" to "Reimposta filtri", + "members.clickTtRequestAction" to "Richiedi autorizzazione Click-TT", + "members.clickTtRequestConfirm" to "Vuoi avviare la richiesta Click-TT automatizzata per {name}?", + "members.clickTtRequestError" to "Impossibile inviare la richiesta Click-TT.", + "members.clickTtRequestFailedLog" to "Richiesta Click-TT non riuscita", + "members.clickTtRequestHint" to "La richiesta viene elaborata automaticamente dal workflow Click-TT del backend.", + "members.clickTtRequestPending" to "Richiesta Click-TT in corso", + "members.clickTtRequestPendingShort" to "In corso...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Accedi ancora a click-tt e invia lì la richiesta.", + "members.clickTtRequestTitle" to "Avvia richiesta Click-TT", + "members.clickTtSubmitted" to "Click-TT inviato", + "members.closeEditor" to "Chiudi editor", + "members.composeEmail" to "Prepara e-mail", + "members.contact" to "Contatto", + "members.copyContactSummary" to "Copia riepilogo contatti", + "members.copyContactSummarySuccess" to "Riepilogo contatti copiato negli appunti.", + "members.copyEmails" to "Copia e-mail", + "members.copyEmailsEmpty" to "Nessun indirizzo e-mail nella selezione attuale.", + "members.copyEmailsSuccess" to "Elenco e-mail copiato negli appunti.", + "members.copyPhones" to "Copia telefoni", + "members.copyPhonesEmpty" to "Nessun numero di telefono nella selezione attuale.", + "members.copyPhonesSuccess" to "Elenco telefonico copiato negli appunti.", + "members.create" to "Crea", + "members.createNewMember" to "Crea nuovo membro", + "members.dataIssueAddress" to "Indirizzo mancante", + "members.dataIssueBirthdate" to "Data di nascita mancante", + "members.dataIssueCity" to "Città mancante", + "members.dataIssueEmail" to "E-mail mancante", + "members.dataIssueGender" to "Genere non definito", + "members.dataIssuePhone" to "Telefono mancante", + "members.dataIssuePostalCode" to "CAP mancante", + "members.dataIssueStreet" to "Via mancante", + "members.dataIssueTrainingGroup" to "Gruppo di allenamento mancante", + "members.dataQuality" to "Qualità dei dati", + "members.dataQualityComplete" to "Dati completi", + "members.deactivateMember" to "Disattiva membro", + "members.deactivateMemberConfirm" to "Vuoi davvero disattivare \"{name}\"?", + "members.deactivateMemberTitle" to "Disattiva membro", + "members.deleteImageConfirm" to "Vuoi davvero eliminare questa immagine?", + "members.deleteImageTitle" to "Elimina immagine", + "members.editHint" to "Un clic su una riga apre l’editor.", + "members.editMember" to "Modifica membro", + "members.editorAssignTrainingGroupHint" to "Assegna almeno un gruppo di allenamento.", + "members.editorCreateHint" to "Crea un nuovo membro e inserisci subito i dati di contatto.", + "members.editorEditHint" to "Modifica i dati di {name}.", + "members.editorRecommendedEntry" to "Voce consigliata", + "members.emailAddress" to "Indirizzo e-mail", + "members.emailAddressShort" to "E-mail", + "members.emails" to "Indirizzi e-mail", + "members.errorAddingToGroup" to "Errore durante l’aggiunta al gruppo", + "members.errorDeactivatingMember" to "Errore durante la disattivazione del membro", + "members.errorDeletingImage" to "Impossibile eliminare l’immagine", + "members.errorLoadingImage" to "Errore durante il caricamento dell’immagine", + "members.errorLoadingMembers" to "Impossibile caricare la lista dei membri.", + "members.errorLoadingTrainingParticipations" to "Errore durante il caricamento delle partecipazioni", + "members.errorMarkingForm" to "Errore durante la marcatura del modulo.", + "members.errorRemovingFromGroup" to "Errore durante la rimozione dal gruppo", + "members.errorRemovingTestMembership" to "Errore durante la rimozione dell’iscrizione di prova.", + "members.errorRotatingImage" to "Errore durante la rotazione dell’immagine", + "members.errorSavingMember" to "Errore durante il salvataggio del membro", + "members.errorSettingPrimaryImage" to "Impossibile impostare l’immagine principale", + "members.errorTransfer" to "Errore durante il trasferimento", + "members.errorUpdatingRatings" to "Errore durante l’aggiornamento dei valori TTR/QTTR", + "members.errorUploadingImage" to "Impossibile caricare l’immagine", + "members.exercises" to "Esercizi", + "members.exportCsv" to "Esporta CSV", + "members.exportCsvFile" to "selezione-membri.csv", + "members.exportEmails" to "E-mail", + "members.exportMembersSelected" to "membri attualmente selezionati", + "members.exportPhones" to "Telefono", + "members.exportPreview" to "Anteprima esportazione", + "members.exportPreviewEmpty" to "Nessun membro nella selezione attuale", + "members.exportPreviewNames" to "Anteprima", + "members.exportReachableByEmail" to "con indirizzo e-mail", + "members.exportReachableByPhone" to "con numero di telefono", + "members.firstName" to "Nome", + "members.formHandedOver" to "Modulo di iscrizione consegnato", + "members.formMarkedAsHandedOver" to "Modulo di iscrizione contrassegnato come consegnato.", + "members.gender" to "Genere", + "members.genderDiverse" to "Diverso", + "members.genderFemale" to "Femminile", + "members.genderMale" to "Maschile", + "members.genderUnknown" to "Sconosciuto", + "members.generatePhoneList" to "Genera elenco telefonico", + "members.groupPhotoCrop" to "Elabora foto di gruppo", + "members.groupPhotoCropSaved" to "Foto del membro salvata dalla foto di gruppo.", + "members.image" to "Immagine", + "members.imageDeleted" to "Immagine eliminata.", + "members.imageInternet" to "Immagine (web?)", + "members.imagePreview" to "Anteprima immagine membro", + "members.imageUpdated" to "Immagine aggiornata", + "members.inactive" to "inattivo", + "members.inactiveMembers" to "Membri inattivi", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "U17 (17 anni o meno)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Cognome", + "members.lastTrainingFilter" to "Ultimo allenamento", + "members.lastTrainingFilterHasDate" to "Con data registrata", + "members.lastTrainingFilterHint" to "Tabella: stagione corrente nella colonna AK. Dettaglio completo (entrambe le stagioni, ultimo allenamento, partecipazioni) al passaggio del mouse sulla riga.", + "members.lastTrainingFilterNoDate" to "Senza ultimo allenamento", + "members.lastTrainingFilterNotInTraining" to "« Non più in allenamento »", + "members.loadingMembers" to "Caricamento membri...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Segna modulo come verificato", + "members.markFormsForSelection" to "Verifica moduli della selezione", + "members.markRegular" to "Segna come regolare", + "members.markRegularForSelection" to "Segna la selezione come regolare", + "members.memberDeactivated" to "Membro disattivato", + "members.memberDetails" to "Dettagli membro", + "members.memberFormHandedOver" to "Modulo di iscrizione consegnato", + "members.memberImage" to "Immagine membro", + "members.memberImages" to "Immagini membro", + "members.memberInfo" to "Informazioni membro", + "members.memberScope" to "Ambito membri", + "members.missingTtrHistoryId" to "Nessun ID myTischtennis memorizzato", + "members.name" to "Cognome, nome", + "members.newMember" to "Nuovo membro", + "members.noAddressShort" to "Nessun indirizzo", + "members.noEmailShort" to "Nessuna e-mail", + "members.noGroupsAssigned" to "Nessun gruppo assegnato", + "members.noGroupsAvailable" to "Nessun gruppo disponibile", + "members.noOpenTasks" to "Nessuna attività aperta", + "members.noPhoneShort" to "Nessun telefono", + "members.notes" to "Note", + "members.noTestMembership" to "Non più membro in prova", + "members.notInTrainingTooltip" to "Nessuna partecipazione da {weeks} settimane di allenamento", + "members.onlyActiveMembers" to "Sono inclusi solo i membri attivi", + "members.openTasks" to "Attività aperte", + "members.parent" to "Genitore", + "members.parentFallback" to "Genitore", + "members.parentName" to "Nome (es. madre, padre)", + "members.phoneList" to "elenco-telefonico.pdf", + "members.phoneListForSelection" to "Elenco telefonico per la selezione", + "members.phoneListSelectionFile" to "elenco-telefonico-selezione.pdf", + "members.phoneNumber" to "Numero di telefono", + "members.phoneNumberShort" to "Tel.", + "members.phones" to "Numeri di telefono", + "members.picsInInternetAllowed" to "Foto consentite online", + "members.postalCode" to "CAP", + "members.previewLastTraining" to "Ultimo allenamento", + "members.previewNoLastTraining" to "Ancora nessuna partecipazione", + "members.primary" to "Principale", + "members.primaryImageUpdated" to "Immagine principale aggiornata.", + "members.remove" to "Rimuovi", + "members.resultsVisible" to "membri visibili", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Attivi", + "members.scopeActiveDataIncomplete" to "Attivo + dati incompleti", + "members.scopeActiveTest" to "Attivo + prova", + "members.scopeAll" to "Tutti", + "members.scopeDataIncomplete" to "Dati incompleti", + "members.scopeInactive" to "Inattivi", + "members.scopeNeedsForm" to "Modulo non verificato", + "members.scopeNotTraining" to "Non si allena più", + "members.scopeTest" to "Prova", + "members.search" to "Cerca", + "members.searchAndFilter" to "Ricerca e filtri", + "members.searchPlaceholder" to "Cerca per nome, città, telefono o e-mail", + "members.selectFile" to "Seleziona file", + "members.showInactiveMembers" to "Mostra membri inattivi", + "members.showTrainingParticipationsColumn" to "Mostra colonna « Partecipazioni »", + "members.showTtrHistory" to "Mostra cronologia TTR", + "members.sixOrMoreParticipations" to "6 o più partecipazioni agli allenamenti", + "members.sortAge" to "Età", + "members.sortBirthday" to "Data di nascita", + "members.sortBy" to "Ordina per", + "members.sortFirstName" to "Nome", + "members.sortLastName" to "Cognome", + "members.sortLastTraining" to "Ultimo allenamento", + "members.sortOpenTasks" to "Attività aperte", + "members.sortQttr" to "Valore QTTR", + "members.status" to "Stato", + "members.street" to "Via", + "members.subtitle" to "Cerca, filtra e modifica direttamente i membri.", + "members.taskActionMarkRegular" to "Segna come regolare", + "members.taskActionRequest" to "Richiedi", + "members.taskActionReview" to "Apri", + "members.taskActionVerify" to "Verifica", + "members.taskAssignTrainingGroup" to "Assegna gruppo di allenamento", + "members.taskCheckClickTt" to "Controlla autorizzazione Click-TT", + "members.taskCheckDataQuality" to "Controlla qualità dati", + "members.taskCheckTrainingStatus" to "Controlla stato allenamento", + "members.taskReviewTrialStatus" to "Controlla stato di prova", + "members.taskVerifyForm" to "Verifica modulo", + "members.testMember" to "Prova", + "members.testMembers" to "Membri in prova", + "members.testMembership" to "Iscrizione di prova", + "members.testMembershipRemoved" to "Iscrizione di prova rimossa.", + "members.threeOrMoreParticipations" to "3 o più partecipazioni agli allenamenti", + "members.title" to "Membri", + "members.toggleSortDirection" to "Cambia direzione di ordinamento", + "members.trainingGroups" to "Gruppi di allenamento", + "members.trainingParticipations" to "Partecipazioni agli allenamenti", + "members.transferErrorTitle" to "Errore di trasferimento", + "members.transferMembers" to "Trasferisci membri", + "members.transferSuccessTitle" to "Trasferimento riuscito", + "members.ttAdult" to "Adulti (non giovanili secondo la data)", + "members.ttAgeClassCol" to "Classe (TT)", + "members.ttFilterGroupJ" to "Ragazze e ragazzi (misto)", + "members.ttFilterGroupM" to "Solo ragazze", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "attuale", + "members.ttSeasonFilter" to "Stagione (data di riferimento)", + "members.ttSeasonNextTag" to "prossima", + "members.ttStichtagHint" to "Data di riferimento 1° gennaio (DTTB). Ragazzi: solo classi J. Ragazze: J e M.", + "members.updateRatings" to "Aggiorna TTR/QTTR da myTischtennis", + "members.updating" to "Aggiornamento in corso...", + "members.visibleMembers" to "Membri visibili", + "members.wantsToPlay" to "Vuole giocare", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Annulla", + "messages.confirm" to "Conferma", + "messages.error" to "Errore", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Informazione", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Successo", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Avviso", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Approvazioni", + "navigation.backToHome" to "Torna alla home", + "navigation.billing" to "Fatturazione", + "navigation.clickTtAccount" to "Account HTTV / click-TT", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Impostazioni del club", + "navigation.clubTournaments" to "Tornei del club", + "navigation.competitions" to "Competizioni", + "navigation.dailyBusiness" to "Gestione quotidiana", + "navigation.diary" to "Diario", + "navigation.home" to "Home", + "navigation.login" to "Accedi", + "navigation.logout" to "Disconnetti", + "navigation.logs" to "Log di sistema", + "navigation.members" to "Membri", + "navigation.memberTransfer" to "Trasferimento membri", + "navigation.myTischtennisAccount" to "Account myTischtennis", + "navigation.orders" to "Ordini", + "navigation.permissions" to "Permessi", + "navigation.personalSettings" to "Impostazioni personali", + "navigation.predefinedActivities" to "Attività predefinite", + "navigation.register" to "Registrati", + "navigation.schedule" to "Calendari", + "navigation.settings" to "Impostazioni", + "navigation.statistics" to "Statistiche di allenamento", + "navigation.teamManagement" to "Gestione squadre", + "navigation.tournamentParticipations" to "Partecipazioni ai tornei", + "navigation.tournaments" to "Tornei", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Crea ordine", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Costo", + "orders.dateAutoHint" to "La data viene impostata automaticamente e ogni modifica viene registrata con una data.", + "orders.errorLoading" to "Impossibile caricare gli ordini.", + "orders.errorSaving" to "Impossibile salvare l'ordine.", + "orders.filterAllClubs" to "Tutti i club", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Tutti gli stati", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Qui è possibile visualizzare e gestire tutti gli ordini di tutti i club.", + "orders.globalTitle" to "Ordini di tutti i club", + "orders.history" to "Cronologia", + "orders.item" to "Articolo", + "orders.itemPlaceholder" to "ad es. maglia, felpa o custodia per racchetta", + "orders.loading" to "Caricamento ordini...", + "orders.member" to "Membro", + "orders.memberTitle" to "Ordini: {name}", + "orders.noOrdersGlobal" to "Attualmente non ci sono ordini.", + "orders.noOrdersMember" to "Non ci sono ancora ordini per questo membro.", + "orders.open" to "Da pagare", + "orders.orderDate" to "Creato il", + "orders.paid" to "Pagato", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Cerca per club, membro o articolo", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Stato", + "orders.statusArrived" to "articolo arrivato", + "orders.statusDate" to "Ultima modifica", + "orders.statusHandedOver" to "articolo consegnato", + "orders.statusOrdered" to "ordinato", + "orders.statusRequested" to "richiesto", + "orders.title" to "Ordini", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Società:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Creato:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Campionato:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Iscrizione per:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Campionato previsto:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Stagione:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Categoria età squadra:", + "pdfGenerator.teamGenderLabel" to "Genere squadra:", + "pdfGenerator.teamLineupTitle" to "Formazione della squadra", + "pdfGenerator.teamNameLabel" to "Squadra:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Lingua", + "settings.languageChanged" to "Lingua modificata con successo", + "settings.languageDescription" to "Scegli la tua lingua preferita per l'applicazione", + "settings.personalSettings" to "Impostazioni personali", + "settings.selectLanguage" to "Seleziona lingua", + "settings.title" to "Impostazioni", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Giocatori disponibili", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Idoneità", + "teamManagement.eligibilityAdultRelease" to "Autorizzato per adulti", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Autorizzato + riserva negli adulti", + "teamManagement.eligibilityAdultReserve" to "Riserva negli adulti", + "teamManagement.eligibilityRegular" to "Regolare", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Esporta formazione come PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Girone d’andata", + "teamManagement.firstHalfFull" to "Girone d’andata (luglio - dicembre)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Seleziona una classe di campionato e salva.", + "teamManagement.lineupEmpty" to "Nessun giocatore è ancora stato assegnato a questa squadra.", + "teamManagement.lineupPdfEmpty" to "Nessun giocatore in formazione – impossibile creare il PDF.", + "teamManagement.lineupPdfFilePrefix" to "Formazione", + "teamManagement.lineupProposal" to "Formazione in base al QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Impossibile salvare la formazione della squadra.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} ha più di 30 punti QTTR di vantaggio su {lower}. Correggi questo ordine.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Dati mancanti:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Incolla e analizza l’URL MyTischtennis per collegare l’ID squadra e i dati della lega.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Campionato previsto", + "teamManagement.plannedLeagueHint" to "Facoltativo. Indipendente dal campionato registrato (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "es. campionato di zona, dopo la prossima iscrizione …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Girone di ritorno", + "teamManagement.secondHalfFull" to "Girone di ritorno (dal 1° gennaio)", + "teamManagement.selectedLineup" to "Giocatori schierati", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Aggiungi regola del girone", + "tournaments.address" to "Indirizzo", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Tutti i dati dei partecipanti sono completi.", + "tournaments.allDataCompleteTop3" to "Tutti i dati dei primi 3 classificati sono completi.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Aggiungi almeno una regola del girone per {label} (es. posizioni 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Correggi", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Crea il turno finale dall'intermedio", + "tournaments.createFinalFromPreliminary" to "Crea il turno finale dal preliminare", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Crea il turno intermedio dal preliminare", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Non ancora registrato", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Vuoi davvero eliminare la classe \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "Tutti i partecipanti saranno scollegati da questa classe.", + "tournaments.deleteClassTitle" to "Elimina classe", + "tournaments.deleteExistingPairingsConfirm" to "Le coppie esistenti verranno eliminate. Continuare?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Distribuire tavoli liberi", + "tournaments.distributeTablesResult" to "Distribuzione dei tavoli", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Adatto per: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Inserisci un nome classe.", + "tournaments.enterExternalParticipantName" to "Inserisci almeno nome e cognome.", + "tournaments.enterMiniLocation" to "Inserisci una località.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Errore durante la distribuzione dei tavoli.", + "tournaments.errorGeneratingPdf" to "Errore durante la generazione del PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Turno finale", + "tournaments.finishMatch" to "Concludi", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Generazione PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Gruppi", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Turno intermedio (turno 2)", + "tournaments.internalStatsAbsoluteRank" to "Classifica somma", + "tournaments.internalStatsAgeFilter" to "Età e genere (singolo)", + "tournaments.internalStatsAgeFilterAll" to "Tutte le categorie", + "tournaments.internalStatsAgeFilterNone" to "Nessuna categoria selezionata", + "tournaments.internalStatsAgeNoClass" to "Senza classe assegnata", + "tournaments.internalStatsAgeSelectAll" to "Tutte", + "tournaments.internalStatsAgeSelectNone" to "Nessuna", + "tournaments.internalStatsAverageRank" to "Classifica media (per torneo)", + "tournaments.internalStatsAvgPoints" to "Media", + "tournaments.internalStatsEmpty" to "Nessun dato nel periodo selezionato.", + "tournaments.internalStatsExportPdf" to "Esporta PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Ultimi 12 mesi", + "tournaments.internalStatsLast3Months" to "Ultimi 3 mesi", + "tournaments.internalStatsLast6Months" to "Ultimi 6 mesi", + "tournaments.internalStatsOpenButton" to "Statistiche tornei (singolo)", + "tournaments.internalStatsPeriod" to "Periodo", + "tournaments.internalStatsPoints" to "Totale", + "tournaments.internalStatsPointsExplain" to "Punteggio: in ogni girone la posizione è in percentuale (con N classificati: 1° = 100 %, ultimo = 0 %, lineare in mezzo; stesso posto = stesso valore). N include tutti i classificati nel girone (ospiti inclusi). Un solo giocatore: 100 %. In KO: massimo girone della classe + 1, poi +1 per partita KO vinta. Solo soci al singolo.", + "tournaments.internalStatsTitle" to "Statistiche tornei interni (singolo)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} torneo/i nel periodo (esclusi i mini-campionati).", + "tournaments.internalStatsTtAdult" to "Adulti", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Segna come live", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Sono necessari almeno 2 partecipanti per le coppie.", + "tournaments.missingDataPDF" to "Dati mancanti come PDF", + "tournaments.missingDataPDFSubtitle" to "Si prega di raccogliere i dati mancanti (contrassegnati con ____) dai partecipanti e annotarli qui.", + "tournaments.missingDataPDFSubtitleTop3" to "Si prega di raccogliere i dati mancanti dei primi 3 (contrassegnati con ____) e annotarli qui.", + "tournaments.missingDataPDFTitle" to "Dati mancanti dei partecipanti – Mini campionato", + "tournaments.missingDataPDFTitleTop3" to "Dati mancanti – Top 3 Mini campionato", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Nuovo set, es. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Nessuna partita disponibile in cui entrambi i giocatori sono liberi.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "Nessuna classe adatta trovata.", + "tournaments.noFreeTables" to "Nessun tavolo libero disponibile.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Nessuna partita orfana trovata.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Nessuna regola ancora. Esempio: posizioni 1 e 2 -> gruppi superiori del turno 2.", + "tournaments.noPoolRulesYetFinal" to "Nessuna regola ancora. Esempio: posizioni 1 e 2 -> turno finale.", + "tournaments.noTop3Yet" to "I primi 3 posti non sono ancora stati determinati.", + "tournaments.noTournamentDate" to "Nessuna data torneo disponibile.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Nessuna sessione di allenamento trovata per {date}.", + "tournaments.noTrainingParticipants" to "Nessun partecipante trovato nella sessione di allenamento per questa data.", + "tournaments.noValidTrainingParticipants" to "Nessun partecipante valido trovato nella sessione di allenamento per questa data.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Numero di tavoli", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} partite orfane rimosse.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Pagina", + "tournaments.pairingAlreadyExists" to "Questa coppia esiste già.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} coppie create, {errorCount} errori.", + "tournaments.participantConflicts" to "Conflitti", + "tournaments.participantNotFound" to "Partecipante non trovato.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Telefono", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Posizioni da ogni gruppo (es. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Giocatore 1", + "tournaments.playerTwo" to "Giocatore 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Giocare la finale per il terzo posto", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Avanzamento: turno preliminare → turno intermedio (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Avanzamento: turno {from} → turno {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Classifica", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Numero di gruppi", + "tournaments.roundMode" to "Modalità", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Salva turni", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Seleziona prima un torneo.", + "tournaments.selectTwoDifferentPlayers" to "Seleziona due giocatori diversi.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Risposta del server non valida.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "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.", + "tournaments.stageConfigLoadError" to "Errore durante il caricamento della configurazione dei turni.", + "tournaments.stageConfigLoading" to "Caricamento configurazione dei turni…", + "tournaments.stageConfigMissingIds" to "Impossibile salvare: manca l'ID del club o del torneo.", + "tournaments.stageConfigTitle" to "Turno intermedio e finale", + "tournaments.stageCreated" to "Il turno {round} è stato creato.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Finito", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Aperto", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Tavolo", + "tournaments.tablesDistributed" to "I tavoli sono stati distribuiti.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Destinazione", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Numero di gruppi di destinazione", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Femminile", + "tournaments.tournamentClassGenderOpen" to "Aperta (tutti)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Rimuovi indicatore live", + "tournaments.unpairedDoubles" to "Doppi senza partner", + "tournaments.useIntermediateStage" to "Usa turno intermedio", + "tournaments.warningBirthDateMissing" to "Manca la data di nascita per questa classe", + "tournaments.warningClassMissing" to "Classe non trovata", + "tournaments.warningGenderMismatch" to "Il genere non corrisponde alla classe", + "tournaments.warningGenderMissing" to "Manca il genere per questa classe", + "tournaments.warningMissingPairing" to "Manca il partner di doppio", + "tournaments.warningTooOldForClass" to "Troppo grande per questa classe", + "tournaments.warningTooYoungForClass" to "Troppo giovane per questa classe", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Azioni", + "trainingStats.activeMembers" to "Membri attivi", + "trainingStats.allTrainingDays" to "Tutti i giorni di allenamento", + "trainingStats.attendingMembers" to "Membri presenti", + "trainingStats.averageParticipationCurrentMonth" to "Partecipazione media (mese corrente)", + "trainingStats.averageParticipationHalfYear" to "Partecipazione media (semestre)", + "trainingStats.averageParticipationLastMonth" to "Partecipazione media (mese precedente)", + "trainingStats.averageParticipationQuarter" to "Partecipazione media (trimestre)", + "trainingStats.averageParticipationYear" to "Partecipazione media (anno)", + "trainingStats.birthdate" to "Data di nascita", + "trainingStats.date" to "Data", + "trainingStats.lastTraining" to "Ultimo allenamento", + "trainingStats.memberParticipations" to "Partecipazioni dei membri", + "trainingStats.name" to "Nome", + "trainingStats.noParticipants" to "Nessun partecipante", + "trainingStats.participants" to "Partecipanti", + "trainingStats.participations12Months" to "Partecipazioni (12 mesi)", + "trainingStats.participations3Months" to "Partecipazioni (3 mesi)", + "trainingStats.participationsTotal" to "Partecipazioni (totale)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Mostra dettagli", + "trainingStats.title" to "Statistiche di allenamento", + "trainingStats.trainingDayFilter" to "Giorno di allenamento", + "trainingStats.trainingDays" to "Giorni di allenamento (ultimi 12 mesi)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Giorno della settimana", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val ja: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "トレーニング日記", + "app.title" to "トレーニング日記", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "アクティベーションに失敗しました。リンクを確認してください。", + "auth.confirmPassword" to "パスワードを確認", + "auth.email" to "メール", + "auth.forgotPassword" to "パスワードをお忘れですか?", + "auth.forgotPasswordDescription" to "メールアドレスを入力してください。パスワードリセット用のリンクが送信されます。", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "ログイン", + "auth.loginFailed" to "ログインに失敗しました。認証情報を確認してください。", + "auth.loginSuccess" to "ログインに成功しました", + "auth.logout" to "ログアウト", + "auth.logoutSuccess" to "ログアウトに成功しました", + "auth.newPassword" to "新しいパスワード", + "auth.noAccount" to "アカウントをお持ちでないですか?", + "auth.password" to "パスワード", + "auth.passwordResetSuccess" to "パスワードが正常に変更されました。新しいパスワードでログインできます。", + "auth.passwordsDoNotMatch" to "パスワードが一致しません。", + "auth.passwordTooShort" to "パスワードは6文字以上必要です。", + "auth.register" to "登録", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "ログイン状態を保持", + "auth.resetEmailSent" to "このメールアドレスのアカウントが存在する場合、リセットリンクが送信されました。受信トレイ(迷惑メールフォルダも)を確認してください。", + "auth.resetFailed" to "パスワードを変更できませんでした。リンクの有効期限が切れている可能性があります。", + "auth.resetPassword" to "新しいパスワードを設定", + "auth.resetRequestFailed" to "リクエストに失敗しました。再試行してください。", + "auth.saveNewPassword" to "パスワードを保存", + "auth.saving" to "保存中...", + "auth.sending" to "送信中...", + "auth.sendResetLink" to "リンクを送信", + "auth.sessionExpired" to "セッションが期限切れです。ログアウトされます。", + "auth.toLogin" to "ログインへ", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "このクラブへのアクセスは許可されていません。", + "club.accessRequested" to "アクセスを申請しました。", + "club.accessRequestFailed" to "アクセス申請を送信できませんでした。", + "club.accessRequestPending" to "このクラブへのアクセスを申請しました。しばらくお待ちください。", + "club.create" to "クラブを作成", + "club.createTitle" to "クラブを作成", + "club.diary" to "練習日誌", + "club.errorLoadingRequests" to "保留中の申請の読み込みエラー", + "club.load" to "読み込む", + "club.members" to "メンバー", + "club.mobileSelectHint" to "スマートフォンでアプリを使用するには、まずクラブを選択してください。", + "club.name" to "クラブ名", + "club.new" to "新しいクラブ", + "club.noAccess" to "このクラブへのアクセス権はまだ付与されていません。", + "club.openAccessRequests" to "保留中のアクセス申請", + "club.openRequests" to "保留中のアクセス申請", + "club.requestAccess" to "アクセスを申請", + "club.select" to "クラブを選択", + "club.selectPlaceholder" to "クラブを選択...", + "club.title" to "クラブ", + "club.trainingDiary" to "練習日誌", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "アクション", + "common.active" to "アクティブ", + "common.add" to "追加", + "common.all" to "すべて", + "common.apply" to "適用", + "common.back" to "戻る", + "common.cancel" to "キャンセル", + "common.choose" to "選択", + "common.clear" to "クリア", + "common.close" to "閉じる", + "common.confirm" to "確認", + "common.create" to "作成", + "common.date" to "日付", + "common.days" to "日", + "common.delete" to "削除", + "common.description" to "説明", + "common.details" to "詳細", + "common.disabled" to "無効", + "common.download" to "ダウンロード", + "common.edit" to "編集", + "common.enabled" to "有効", + "common.filter" to "フィルター", + "common.hours" to "時間", + "common.in" to "後", + "common.inactive" to "非アクティブ", + "common.loading" to "読み込み中...", + "common.min" to "分", + "common.minutes" to "分", + "common.months" to "か月", + "common.move" to "移動", + "common.name" to "名前", + "common.new" to "新規", + "common.next" to "次へ", + "common.no" to "いいえ", + "common.ok" to "OK", + "common.optional" to "任意", + "common.period" to "期間", + "common.previous" to "前へ", + "common.refresh" to "再読み込み", + "common.remove" to "削除", + "common.required" to "必須", + "common.reset" to "リセット", + "common.save" to "保存", + "common.saved" to "保存済み", + "common.saving" to "保存中...", + "common.search" to "検索", + "common.select" to "選択", + "common.status" to "状態", + "common.submit" to "送信", + "common.time" to "時刻", + "common.today" to "今日", + "common.type" to "種類", + "common.update" to "更新", + "common.view" to "表示", + "common.weeks" to "週間", + "common.years" to "年", + "common.yes" to "はい", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "打球を追加", + "courtDrawingTool.backhand" to "バックハンド", + "courtDrawingTool.codeLabel" to "略称", + "courtDrawingTool.completeFirstStroke" to "第1打を完成させる", + "courtDrawingTool.completeFirstStrokeHint" to "開始位置、打球面、回転、落点を選択すると、図が表示されます。", + "courtDrawingTool.configureExercise" to "練習を設定", + "courtDrawingTool.counterSpin" to "逆回転", + "courtDrawingTool.forehand" to "フォアハンド", + "courtDrawingTool.noAdditionalStrokes" to "まだ後続の打球はありません。", + "courtDrawingTool.preview" to "プレビュー", + "courtDrawingTool.previewHint" to "最初の打球が完全に設定されると図が表示されます。", + "courtDrawingTool.service" to "サーブ:", + "courtDrawingTool.serviceTitle" to "サーブ", + "courtDrawingTool.sidespin" to "横回転", + "courtDrawingTool.sideUnderspin" to "横下回転", + "courtDrawingTool.spin" to "回転:", + "courtDrawingTool.startLeft" to "左", + "courtDrawingTool.startMiddle" to "中央", + "courtDrawingTool.startRight" to "右", + "courtDrawingTool.stepAdditionalStrokes" to "4. 後続の打球", + "courtDrawingTool.stepAdditionalStrokesHint" to "必要に応じて後続のボールを一覧で追加できます。", + "courtDrawingTool.stepFirstStroke" to "2. 第1打", + "courtDrawingTool.stepFirstStrokeHint" to "最初のボールの打球面と回転を設定します。", + "courtDrawingTool.stepStartPosition" to "1. 開始位置", + "courtDrawingTool.stepStartPositionHint" to "どのサーブ位置から練習を始めますか?", + "courtDrawingTool.stepTarget" to "3. 落点", + "courtDrawingTool.stepTargetHint" to "第1打の落点ゾーンを選択します。", + "courtDrawingTool.strokeSide" to "面", + "courtDrawingTool.strokeTypeBlock" to "ブロック", + "courtDrawingTool.strokeTypeChopDefense" to "カット守備", + "courtDrawingTool.strokeTypeCounter" to "カウンター", + "courtDrawingTool.strokeTypeFlip" to "フリック", + "courtDrawingTool.strokeTypeLabel" to "打球タイプ", + "courtDrawingTool.strokeTypeLobDefense" to "ロブ守備", + "courtDrawingTool.strokeTypePush" to "ツッツキ", + "courtDrawingTool.strokeTypeSmash" to "スマッシュ", + "courtDrawingTool.strokeTypeTopspin" to "トップスピン", + "courtDrawingTool.targetBackhandHalfLong" to "バック半長", + "courtDrawingTool.targetBackhandLong" to "バック奥", + "courtDrawingTool.targetBackhandShort" to "バック短", + "courtDrawingTool.targetForehandHalfLong" to "フォア半長", + "courtDrawingTool.targetForehandLong" to "フォア奥", + "courtDrawingTool.targetForehandShort" to "フォア短", + "courtDrawingTool.targetMiddleHalfLong" to "ミドル半長", + "courtDrawingTool.targetMiddleLong" to "ミドル奥", + "courtDrawingTool.targetMiddleShort" to "ミドル短", + "courtDrawingTool.targetPosition" to "落点位置:", + "courtDrawingTool.targetPositionLabel" to "落点位置", + "courtDrawingTool.title" to "卓球練習図", + "courtDrawingTool.titleLabel" to "タイトル", + "courtDrawingTool.topspin" to "上回転", + "courtDrawingTool.toTarget" to "へ", + "courtDrawingTool.underspin" to "下回転", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "現在の練習日", + "diary.activities" to "アクティビティ", + "diary.activity" to "アクティビティ", + "diary.activityDrawing" to "アクティビティ図", + "diary.activityImage" to "アクティビティ画像", + "diary.activityNotFound" to "アクティビティが見つかりません。リストから選択してください。", + "diary.activityOrTimeblock" to "アクティビティ / 時間ブロック", + "diary.activityPlaceholder" to "アクティビティ", + "diary.activityRequired" to "アクティビティを入力してください。", + "diary.addActivity" to "アクティビティを追加", + "diary.addGroup" to "グループを追加", + "diary.addGroupActivity" to "グループアクティビティを追加", + "diary.addGroupButton" to "+ グループ", + "diary.addTimeblock" to "時間ブロック", + "diary.all" to "すべて", + "diary.applySuggestion" to "提案を適用", + "diary.assignParticipants" to "参加者を割り当て", + "diary.assignParticipantsForGroupActivity" to "グループアクティビティの参加者を割り当て", + "diary.assignShort" to "割り当て", + "diary.bookAccident" to "事故を記録", + "diary.confirmDelete" to "削除を確認", + "diary.confirmDeleteDate" to "この日付を本当に削除しますか?", + "diary.confirmDeleteDateDetails" to "関連データもすべて削除されます。", + "diary.confirmDeleteGroup" to "グループ「{name}」を本当に削除しますか?", + "diary.createDate" to "日付を作成", + "diary.createDrawing" to "練習図を作成", + "diary.createGroups" to "グループを作成", + "diary.createNew" to "新規作成", + "diary.createNewDate" to "新しい日付を作成", + "diary.date" to "日付", + "diary.dateCannotBeDeleted" to "日付を削除できません", + "diary.dateCannotBeDeletedDetails" to "まだ内容があります(練習計画、参加者、アクティビティ、事故、メモ)。", + "diary.dateNoLongerCurrent" to "選択した日付は最新ではありません。もう一度お試しください。", + "diary.delete" to "削除", + "diary.deleteDate" to "日付を削除", + "diary.deleteGroup" to "グループを削除", + "diary.duration" to "時間", + "diary.durationExampleLong" to "例: 2x7 または 3*5", + "diary.durationExampleShort" to "例: 2x7", + "diary.durationMinutes" to "時間(分)", + "diary.editActivity" to "アクティビティを編集", + "diary.editGroupActivity" to "グループアクティビティを編集", + "diary.editTrainingTimes" to "練習時間を編集", + "diary.errorCreatingActivity" to "アクティビティ作成エラー", + "diary.errorCreatingGroups" to "グループ作成エラー", + "diary.errorDeletingGroup" to "グループ削除エラー", + "diary.errorLoadingPredefinedActivities" to "事前定義アクティビティの読み込みエラー", + "diary.errorMarkingForm" to "会員フォーム更新エラー", + "diary.errorOccurred" to "エラーが発生しました。もう一度お試しください。", + "diary.existingGroups" to "既存のグループ", + "diary.filterAbsent" to "欠席", + "diary.filterAll" to "すべて", + "diary.filterExcused" to "欠席連絡あり", + "diary.filterPresent" to "出席", + "diary.filterTest" to "体験", + "diary.formHandedOver" to "会員フォーム受け渡し済み", + "diary.formMarkedAsHandedOver" to "会員フォームを受け渡し済みにしました", + "diary.freeActivities" to "自由アクティビティ", + "diary.gallery" to "メンバーギャラリー", + "diary.galleryCreating" to "ギャラリーを作成中…", + "diary.group" to "グループ...", + "diary.groupDeletedSuccessfully" to "グループを削除しました。", + "diary.groupManagement" to "グループ管理", + "diary.groupsCreated" to "{count} 個のグループを作成しました。", + "diary.groupsLabel" to "グループ", + "diary.groupsSection" to "グループ", + "diary.leader" to "担当者", + "diary.min" to "分", + "diary.minutes" to "分", + "diary.mustCreateAtLeastTwoGroups" to "初回作成時は少なくとも 2 つのグループが必要です。", + "diary.nextAppointment" to "次回の予定", + "diary.noActiveTrainingDay" to "練習日が選択されていません。", + "diary.noEntries" to "エントリがありません", + "diary.noFreeActivitiesYet" to "自由アクティビティはまだありません。", + "diary.noParticipants" to "この練習日に参加者がいません。", + "diary.numberOfGroups" to "グループ数", + "diary.oneGroupAdded" to "1 つのグループを追加しました。", + "diary.openPlanItems" to "{count} 件未完了", + "diary.openPlanItemsLabel" to "計画状況", + "diary.overallActivity" to "全体アクティビティ", + "diary.participants" to "参加者", + "diary.participantStatusCancelled" to "キャンセル", + "diary.participantStatusExcused" to "欠席連絡あり", + "diary.participantStatusNone" to "ステータスなし", + "diary.planActivitiesCount" to "計画アクティビティ", + "diary.planAddHint" to "新しい計画項目は上の操作から追加します。", + "diary.planEmptyState" to "練習計画にはまだ何も登録されていません。", + "diary.quickAdd" to "+ クイック追加", + "diary.searchParticipants" to "参加者を検索", + "diary.selectGroup" to "グループを選択...", + "diary.selectGroupAndActivity" to "グループを選択し、アクティビティを入力してください。", + "diary.selectParticipantAndNote" to "参加者を選択し、メモを入力してください。", + "diary.selectTags" to "タグを選択", + "diary.selectTrainingGroup" to "練習グループを選択", + "diary.selectTrainingGroupPlaceholder" to "選択してください...", + "diary.showImage" to "画像/図を表示", + "diary.skipSuggestion" to "提案なしで続行", + "diary.standardActivities" to "標準アクティビティ", + "diary.standardActivityAddError" to "標準アクティビティを追加できませんでした。", + "diary.standardDurationShort" to "分", + "diary.startTime" to "開始時刻", + "diary.statusEmpty" to "この練習日はまだ空です。", + "diary.statusInProgress" to "この練習日は一部のみ準備されています。", + "diary.statusOpenShort" to "未完了", + "diary.statusReady" to "時間と練習計画は設定済みです。", + "diary.statusReadyShort" to "準備完了", + "diary.suggestion" to "提案", + "diary.timeblock" to "時間ブロック", + "diary.timeblocksCount" to "時間ブロック", + "diary.title" to "練習日誌", + "diary.today" to "今日", + "diary.trainingDayAsPDF" to "練習日を PDF でダウンロード", + "diary.trainingDayAsPDFShort" to "練習日を PDF", + "diary.trainingDayChecklist" to "完了チェック", + "diary.trainingDaySection" to "練習日", + "diary.trainingDaySummaryPdfShort" to "参加者一覧を PDF", + "diary.trainingEnd" to "練習終了", + "diary.trainingPlan" to "練習計画", + "diary.trainingPlanAsPDF" to "練習計画を PDF で出力", + "diary.trainingPlanPdfShort" to "進行計画を PDF", + "diary.trainingStart" to "練習開始", + "diary.trainingTimesUpdated" to "練習時間を更新しました。", + "diary.trainingWindow" to "練習時間帯", + "diary.trainingWindowUnset" to "未設定", + "diary.unassignedPlanItems" to "{count} 件未割り当て", + "diary.updateTimes" to "時間を更新", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (ドイツ語)", + "languages.de-CH" to "Schwiizerdütsch (スイスドイツ語)", + "languages.en-AU" to "English (英語(AU))", + "languages.en-GB" to "English (英語(GB))", + "languages.en-US" to "English (英語(US))", + "languages.es" to "Español (スペイン語)", + "languages.fil" to "Filipino (フィリピン語)", + "languages.fr" to "Français (フランス語)", + "languages.it" to "Italiano (イタリア語)", + "languages.ja" to "日本語", + "languages.pl" to "Polski (ポーランド語)", + "languages.th" to "ไทย (タイ語)", + "languages.tl" to "Tagalog (タガログ語)", + "languages.zh" to "中文 (中国語)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "操作", + "members.active" to "アクティブ", + "members.activeMembers" to "アクティブメンバー", + "members.addEmail" to "メールアドレスを追加", + "members.addGroup" to "グループを追加...", + "members.addPhone" to "電話番号を追加", + "members.address" to "住所", + "members.adultReleaseApproved" to "一般参加承認済み", + "members.adultReserveApproved" to "一般予備", + "members.adults" to "一般(20歳以上)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "から", + "members.ageGroup" to "年齢区分", + "members.ageRange" to "年齢範囲", + "members.ageToPlaceholder" to "まで", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "生年月日", + "members.bulkActions" to "一括操作", + "members.camera" to "カメラ", + "members.change" to "変更", + "members.city" to "市区町村", + "members.clearFields" to "入力欄をクリア", + "members.clearFilters" to "フィルターをリセット", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "作成", + "members.createNewMember" to "新規メンバーを作成", + "members.dataIssueAddress" to "住所がありません", + "members.dataIssueBirthdate" to "生年月日がありません", + "members.dataIssueCity" to "市区町村がありません", + "members.dataIssueEmail" to "メールアドレスがありません", + "members.dataIssueGender" to "性別が未設定です", + "members.dataIssuePhone" to "電話番号がありません", + "members.dataIssuePostalCode" to "郵便番号がありません", + "members.dataIssueStreet" to "番地がありません", + "members.dataIssueTrainingGroup" to "練習グループがありません", + "members.dataQuality" to "データ品質", + "members.dataQualityComplete" to "データは完全です", + "members.deactivateMember" to "メンバーを無効化", + "members.deactivateMemberConfirm" to "「{name}」を本当に無効化しますか?", + "members.deactivateMemberTitle" to "メンバーを無効化", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "メンバーを編集", + "members.editorAssignTrainingGroupHint" to "少なくとも 1 つの練習グループを割り当ててください。", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "推奨入力", + "members.emailAddress" to "メールアドレス", + "members.emailAddressShort" to "メール", + "members.emails" to "メールアドレス", + "members.errorAddingToGroup" to "グループへの追加エラー", + "members.errorDeactivatingMember" to "メンバーの無効化エラー", + "members.errorDeletingImage" to "画像を削除できませんでした", + "members.errorLoadingImage" to "画像の読み込みエラー", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "練習参加履歴の読み込みエラー", + "members.errorMarkingForm" to "フォームの更新エラー。", + "members.errorRemovingFromGroup" to "グループからの削除エラー", + "members.errorRemovingTestMembership" to "体験会員の解除エラー。", + "members.errorRotatingImage" to "画像回転エラー", + "members.errorSavingMember" to "メンバーの保存エラー", + "members.errorSettingPrimaryImage" to "メイン画像を設定できませんでした", + "members.errorTransfer" to "移行エラー", + "members.errorUpdatingRatings" to "TTR/QTTR 更新エラー", + "members.errorUploadingImage" to "画像をアップロードできませんでした", + "members.exercises" to "練習", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "名", + "members.formHandedOver" to "会員フォーム受け渡し済み", + "members.formMarkedAsHandedOver" to "会員フォームを受け渡し済みにしました。", + "members.gender" to "性別", + "members.genderDiverse" to "その他", + "members.genderFemale" to "女性", + "members.genderMale" to "男性", + "members.genderUnknown" to "不明", + "members.generatePhoneList" to "電話リストを作成", + "members.groupPhotoCrop" to "集合写真を処理", + "members.groupPhotoCropSaved" to "集合写真から会員写真を保存しました。", + "members.image" to "画像", + "members.imageDeleted" to "画像を削除しました。", + "members.imageInternet" to "画像(公開)", + "members.imagePreview" to "メンバー画像のプレビュー", + "members.imageUpdated" to "画像を更新しました", + "members.inactive" to "非アクティブ", + "members.inactiveMembers" to "非アクティブメンバー", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17(17歳以下)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "姓", + "members.lastTrainingFilter" to "最終参加", + "members.lastTrainingFilterHasDate" to "日付あり", + "members.lastTrainingFilterHint" to "表:AK列は今シーズン。列にマウスを乗せると両シーズン・最終参加・参加回数(分かる場合)を表示。", + "members.lastTrainingFilterNoDate" to "最終参加なし", + "members.lastTrainingFilterNotInTraining" to "「参加なし」フラグ", + "members.loadingMembers" to "Loading members...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "メンバーを無効化しました", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "会員フォーム受け渡し済み", + "members.memberImage" to "メンバー画像", + "members.memberImages" to "メンバー画像", + "members.memberInfo" to "メンバー情報", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "myTischtennis ID が登録されていません", + "members.name" to "姓名", + "members.newMember" to "新規メンバー", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "グループ未割り当て", + "members.noGroupsAvailable" to "利用可能なグループがありません", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "メモ", + "members.noTestMembership" to "体験会員ではない", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "アクティブメンバーのみ出力されます", + "members.openTasks" to "Open tasks", + "members.parent" to "保護者", + "members.parentFallback" to "Parent", + "members.parentName" to "名前(例:母、父)", + "members.phoneList" to "電話リスト.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "電話番号", + "members.phoneNumberShort" to "電話番号", + "members.phones" to "電話番号", + "members.picsInInternetAllowed" to "インターネット掲載可", + "members.postalCode" to "郵便番号", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "メイン", + "members.primaryImageUpdated" to "メイン画像を更新しました。", + "members.remove" to "削除", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "アクティブ + データ不完全", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "データ不完全", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "検索とフィルター", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "ファイルを選択", + "members.showInactiveMembers" to "非アクティブメンバーを表示", + "members.showTrainingParticipationsColumn" to "「参加回数」列を表示", + "members.showTtrHistory" to "TTR 履歴を表示", + "members.sixOrMoreParticipations" to "練習参加 6 回以上", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "QTTR値", + "members.status" to "状態", + "members.street" to "住所", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "開く", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "練習グループを割り当て", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "データ品質を確認", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "体験", + "members.testMembers" to "体験メンバー", + "members.testMembership" to "体験会員", + "members.testMembershipRemoved" to "体験会員を解除しました。", + "members.threeOrMoreParticipations" to "練習参加 3 回以上", + "members.title" to "メンバー", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "練習グループ", + "members.trainingParticipations" to "練習参加回数", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "メンバーを移行", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "一般(基準日でジュニアではない)", + "members.ttAgeClassCol" to "年齢(TT)", + "members.ttFilterGroupJ" to "男女混合", + "members.ttFilterGroupM" to "女子のみ", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "今季", + "members.ttSeasonFilter" to "シーズン(基準日)", + "members.ttSeasonNextTag" to "来季", + "members.ttStichtagHint" to "基準日1月1日(DTTB)。男子:Jのみ。女子:JとM。", + "members.updateRatings" to "myTischtennis から TTR/QTTR を更新", + "members.updating" to "更新中...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "プレー希望", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "キャンセル", + "messages.confirm" to "確認", + "messages.error" to "エラー", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "情報", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "成功", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "警告", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "承認", + "navigation.backToHome" to "ホームへ戻る", + "navigation.billing" to "請求", + "navigation.clickTtAccount" to "HTTV / click-TT アカウント", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "クラブ設定", + "navigation.clubTournaments" to "クラブ大会", + "navigation.competitions" to "競技", + "navigation.dailyBusiness" to "日常業務", + "navigation.diary" to "日記", + "navigation.home" to "ホーム", + "navigation.login" to "ログイン", + "navigation.logout" to "ログアウト", + "navigation.logs" to "システムログ", + "navigation.members" to "メンバー", + "navigation.memberTransfer" to "メンバー転送", + "navigation.myTischtennisAccount" to "myTischtennisアカウント", + "navigation.orders" to "注文", + "navigation.permissions" to "権限", + "navigation.personalSettings" to "個人設定", + "navigation.predefinedActivities" to "事前定義された活動", + "navigation.register" to "登録", + "navigation.schedule" to "スケジュール", + "navigation.settings" to "設定", + "navigation.statistics" to "トレーニング統計", + "navigation.teamManagement" to "チーム管理", + "navigation.tournamentParticipations" to "大会参加", + "navigation.tournaments" to "トーナメント", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "注文を追加", + "orders.budget" to "Budget", + "orders.club" to "クラブ", + "orders.cost" to "費用", + "orders.dateAutoHint" to "日付は自動設定され、すべての変更が日付付きで記録されます。", + "orders.errorLoading" to "注文を読み込めませんでした。", + "orders.errorSaving" to "注文を保存できませんでした。", + "orders.filterAllClubs" to "すべてのクラブ", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "すべての状態", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "ここではクラブ横断で全ての注文を表示・管理できます。", + "orders.globalTitle" to "全クラブの注文", + "orders.history" to "履歴", + "orders.item" to "商品", + "orders.itemPlaceholder" to "例: シャツ、パーカー、ラケットケース", + "orders.loading" to "注文を読み込み中...", + "orders.member" to "メンバー", + "orders.memberTitle" to "注文: {name}", + "orders.noOrdersGlobal" to "現在注文はありません。", + "orders.noOrdersMember" to "このメンバーにはまだ注文がありません。", + "orders.open" to "未払い", + "orders.orderDate" to "登録日", + "orders.paid" to "支払済み", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "クラブ、メンバー、または商品で検索", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "状態", + "orders.statusArrived" to "商品到着", + "orders.statusDate" to "最終変更", + "orders.statusHandedOver" to "商品引き渡し済み", + "orders.statusOrdered" to "注文済み", + "orders.statusRequested" to "希望", + "orders.title" to "注文", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "クラブ:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "作成日時:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "リーグ:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "登録対象:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "予定リーグ:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "シーズン:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "チーム年齢区分:", + "pdfGenerator.teamGenderLabel" to "チーム性別:", + "pdfGenerator.teamLineupTitle" to "チーム登録メンバー", + "pdfGenerator.teamNameLabel" to "チーム:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "言語", + "settings.languageChanged" to "言語が正常に変更されました", + "settings.languageDescription" to "アプリケーションの優先言語を選択してください", + "settings.personalSettings" to "個人設定", + "settings.selectLanguage" to "言語を選択", + "settings.title" to "設定", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "利用可能な選手", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "出場資格", + "teamManagement.eligibilityAdultRelease" to "一般出場許可", + "teamManagement.eligibilityAdultReleaseAndReserve" to "一般出場許可 + 補欠", + "teamManagement.eligibilityAdultReserve" to "一般の補欠", + "teamManagement.eligibilityRegular" to "通常", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "メンバー表をPDFで出力", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "前期", + "teamManagement.firstHalfFull" to "前期(7月 - 12月)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "リーグを選択して保存してください。", + "teamManagement.lineupEmpty" to "このチームにはまだ選手が登録されていません。", + "teamManagement.lineupPdfEmpty" to "登録プレイヤーがいないためPDFを作成できません。", + "teamManagement.lineupPdfFilePrefix" to "メンバー表", + "teamManagement.lineupProposal" to "QTTRによるチーム登録", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "チーム登録を保存できませんでした。", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} は {lower} より 30 QTTR ポイント以上高いです。この順序を修正してください。", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "不足している情報:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "MyTischtennis の URL を貼り付けて解析し、チーム ID とリーグデータを関連付けます。", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "予定リーグ", + "teamManagement.plannedLeagueHint" to "任意。登録済みリーグ(MyTischtennis)とは別です。", + "teamManagement.plannedLeaguePlaceholder" to "例:地区リーグ、次回登録後 …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "後期", + "teamManagement.secondHalfFull" to "後期(1月1日以降)", + "teamManagement.selectedLineup" to "登録済み選手", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "プールルールを追加", + "tournaments.address" to "住所", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "すべての参加者データが揃っています。", + "tournaments.allDataCompleteTop3" to "上位3名のデータはすべて揃っています。", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "{label} に対して少なくとも1つのプールルールを追加してください(例: 1,2位)。", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "修正", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "中間ラウンドから決勝ラウンドを作成", + "tournaments.createFinalFromPreliminary" to "予選から決勝ラウンドを作成", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "予選から中間ラウンドを作成", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "未登録", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "クラス「{name}」を本当に削除しますか?", + "tournaments.deleteClassParticipantsDetached" to "すべての参加者がこのクラスから外されます。", + "tournaments.deleteClassTitle" to "クラスを削除", + "tournaments.deleteExistingPairingsConfirm" to "既存のペアリングは削除されます。続行しますか?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "空き卓を割り当て", + "tournaments.distributeTablesResult" to "卓の割り当て", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "参加可能: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "クラス名を入力してください。", + "tournaments.enterExternalParticipantName" to "少なくとも名と姓を入力してください。", + "tournaments.enterMiniLocation" to "場所を入力してください。", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "卓の割り当て中にエラーが発生しました。", + "tournaments.errorGeneratingPdf" to "PDF生成エラー。", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "決勝ラウンド", + "tournaments.finishMatch" to "終了", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "PDF作成中...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "グループ", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "中間ラウンド(ラウンド2)", + "tournaments.internalStatsAbsoluteRank" to "合計ランキング", + "tournaments.internalStatsAgeFilter" to "年齢・性別(シングルス)", + "tournaments.internalStatsAgeFilterAll" to "すべての年齢クラス", + "tournaments.internalStatsAgeFilterNone" to "年齢クラス未選択", + "tournaments.internalStatsAgeNoClass" to "クラス未設定", + "tournaments.internalStatsAgeSelectAll" to "すべて", + "tournaments.internalStatsAgeSelectNone" to "なし", + "tournaments.internalStatsAverageRank" to "平均ランキング(大会あたり)", + "tournaments.internalStatsAvgPoints" to "平均", + "tournaments.internalStatsEmpty" to "選択した期間にデータがありません。", + "tournaments.internalStatsExportPdf" to "PDFで出力", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "過去12か月", + "tournaments.internalStatsLast3Months" to "過去3か月", + "tournaments.internalStatsLast6Months" to "過去6か月", + "tournaments.internalStatsOpenButton" to "統計を表示(シングルス)", + "tournaments.internalStatsPeriod" to "期間", + "tournaments.internalStatsPoints" to "合計", + "tournaments.internalStatsPointsExplain" to "計算:各グループでは順位をパーセントで表します(順位付きN人:1位=100%、最下位=0%、その間は線形;同順位は同値)。Nはグループ内の順位付き全員(ゲスト含む)。1人のみの場合は100%。KO進出者はクラス内最高のグループ値+1、KO勝利ごとに+1。クラブ会員シングルのみ。", + "tournaments.internalStatsTitle" to "内部大会の統計(シングルス)", + "tournaments.internalStatsTournamentsInPeriod" to "期間内の大会 {count} 件(ミニ選手権は除く)。", + "tournaments.internalStatsTtAdult" to "一般・シニア", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "進行中としてマーク", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "ペアリングには少なくとも2人の参加者が必要です。", + "tournaments.missingDataPDF" to "不足データをPDFで", + "tournaments.missingDataPDFSubtitle" to "不足しているデータ(____で表示)を参加者から収集し、ここに記入してください。", + "tournaments.missingDataPDFSubtitleTop3" to "トップ3の不足データ(____で表示)を収集し、ここに記入してください。", + "tournaments.missingDataPDFTitle" to "参加者の不足データ – ミニ選手権", + "tournaments.missingDataPDFTitleTop3" to "不足データ – トップ3 ミニ選手権", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "新しいセット 例: 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "両選手が空いている試合がありません。", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "適切なクラスが見つかりません。", + "tournaments.noFreeTables" to "空き卓がありません。", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "孤立した試合は見つかりませんでした。", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "まだルールがありません。例: 1位と2位 -> ラウンド2上位グループ。", + "tournaments.noPoolRulesYetFinal" to "まだルールがありません。例: 1位と2位 -> 決勝ラウンド。", + "tournaments.noTop3Yet" to "上位3名がまだ確定していません。", + "tournaments.noTournamentDate" to "大会日がありません。", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "{date} の練習セッションが見つかりませんでした。", + "tournaments.noTrainingParticipants" to "この日付の練習セッションに参加者が見つかりませんでした。", + "tournaments.noValidTrainingParticipants" to "この日付の練習セッションに有効な参加者が見つかりませんでした。", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "卓数", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "孤立した試合を {count} 件削除しました。", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "ページ", + "tournaments.pairingAlreadyExists" to "このペアは既に存在します。", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} 件のペアリングを作成、{errorCount} 件のエラー。", + "tournaments.participantConflicts" to "競合", + "tournaments.participantNotFound" to "参加者が見つかりません。", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "電話", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "各グループからの順位(例: 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "選手 1", + "tournaments.playerTwo" to "選手 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "3位決定戦を行う", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "進出: 予選 → 中間ラウンド (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "進出: ラウンド {from} → ラウンド {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "順位", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "グループ数", + "tournaments.roundMode" to "モード", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "ラウンドを保存", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "先に大会を選択してください。", + "tournaments.selectTwoDifferentPlayers" to "異なる2人の選手を選択してください。", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "サーバー応答が無効です。", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "中間ラウンドは任意です。有効にすると、その後に必ず決勝ラウンドがあります。ノックアウト決勝ラウンドは単一ブラケットとして作成されます。", + "tournaments.stageConfigLoadError" to "ラウンド設定の読み込みエラー。", + "tournaments.stageConfigLoading" to "ラウンド設定を読み込み中…", + "tournaments.stageConfigMissingIds" to "保存できません: クラブIDまたは大会IDがありません。", + "tournaments.stageConfigTitle" to "中間ラウンドと決勝ラウンド", + "tournaments.stageCreated" to "ラウンド {round} を作成しました。", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "終了", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "進行中", + "tournaments.statusOpen" to "未開始", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "卓", + "tournaments.tablesDistributed" to "卓が割り当てられました。", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "対象", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "対象グループ数", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "女子", + "tournaments.tournamentClassGenderOpen" to "オープン(全員)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "進行中マークを解除", + "tournaments.unpairedDoubles" to "ペアのいないダブルス", + "tournaments.useIntermediateStage" to "中間ラウンドを使用", + "tournaments.warningBirthDateMissing" to "このクラスには生年月日が必要です", + "tournaments.warningClassMissing" to "クラスが見つかりません", + "tournaments.warningGenderMismatch" to "性別がクラス条件と一致しません", + "tournaments.warningGenderMissing" to "このクラスには性別情報が必要です", + "tournaments.warningMissingPairing" to "ダブルスのパートナーがいません", + "tournaments.warningTooOldForClass" to "このクラスには年齢が高すぎます", + "tournaments.warningTooYoungForClass" to "このクラスには年齢が低すぎます", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "操作", + "trainingStats.activeMembers" to "アクティブメンバー", + "trainingStats.allTrainingDays" to "すべての練習日", + "trainingStats.attendingMembers" to "参加した会員", + "trainingStats.averageParticipationCurrentMonth" to "平均参加数(今月)", + "trainingStats.averageParticipationHalfYear" to "平均参加数(半年)", + "trainingStats.averageParticipationLastMonth" to "平均参加数(先月)", + "trainingStats.averageParticipationQuarter" to "平均参加数(四半期)", + "trainingStats.averageParticipationYear" to "平均参加数(年間)", + "trainingStats.birthdate" to "生年月日", + "trainingStats.date" to "日付", + "trainingStats.lastTraining" to "最終練習", + "trainingStats.memberParticipations" to "メンバー参加数", + "trainingStats.name" to "名前", + "trainingStats.noParticipants" to "参加者なし", + "trainingStats.participants" to "参加者", + "trainingStats.participations12Months" to "参加回数(12 か月)", + "trainingStats.participations3Months" to "参加回数(3 か月)", + "trainingStats.participationsTotal" to "参加回数(合計)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "詳細を表示", + "trainingStats.title" to "練習統計", + "trainingStats.trainingDayFilter" to "練習日", + "trainingStats.trainingDays" to "練習日(過去 12 か月)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "曜日", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val pl: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Dziennik treningowy", + "app.title" to "Dziennik treningowy", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Aktywacja nie powiodła się. Sprawdź link.", + "auth.confirmPassword" to "Potwierdź hasło", + "auth.email" to "Email", + "auth.forgotPassword" to "Zapomniałeś hasła?", + "auth.forgotPasswordDescription" to "Podaj swój adres e-mail. Otrzymasz link do zresetowania hasła.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Zaloguj", + "auth.loginFailed" to "Logowanie nie powiodło się. Sprawdź dane logowania.", + "auth.loginSuccess" to "Pomyślnie zalogowano", + "auth.logout" to "Wyloguj", + "auth.logoutSuccess" to "Pomyślnie wylogowano", + "auth.newPassword" to "Nowe hasło", + "auth.noAccount" to "Nie masz konta?", + "auth.password" to "Hasło", + "auth.passwordResetSuccess" to "Hasło zostało zmienione pomyślnie. Możesz się teraz zalogować.", + "auth.passwordsDoNotMatch" to "Hasła nie są zgodne.", + "auth.passwordTooShort" to "Hasło musi mieć co najmniej 6 znaków.", + "auth.register" to "Zarejestruj", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Zapamiętaj mnie", + "auth.resetEmailSent" to "Jeśli konto z tym adresem istnieje, wysłano link do resetowania. Sprawdź skrzynkę (i spam).", + "auth.resetFailed" to "Nie udało się zmienić hasła. Link mógł wygasnąć.", + "auth.resetPassword" to "Ustaw nowe hasło", + "auth.resetRequestFailed" to "Żądanie nie powiodło się. Spróbuj ponownie.", + "auth.saveNewPassword" to "Zapisz hasło", + "auth.saving" to "Zapisywanie...", + "auth.sending" to "Wysyłanie...", + "auth.sendResetLink" to "Wyślij link", + "auth.sessionExpired" to "Twoja sesja wygasła. Zostaniesz wylogowany.", + "auth.toLogin" to "Do logowania", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Dostęp do klubu został odrzucony.", + "club.accessRequested" to "Poproszono o dostęp.", + "club.accessRequestFailed" to "Nie udało się wysłać prośby o dostęp.", + "club.accessRequestPending" to "Poproszono o dostęp do tego klubu. Proszę czekać.", + "club.create" to "Utwórz klub", + "club.createTitle" to "Utwórz klub", + "club.diary" to "Dziennik treningowy", + "club.errorLoadingRequests" to "Błąd podczas ładowania otwartych próśb", + "club.load" to "Załaduj", + "club.members" to "Członkowie", + "club.mobileSelectHint" to "Najpierw wybierz klub, aby korzystać z aplikacji na smartfonie.", + "club.name" to "Nazwa klubu", + "club.new" to "Nowy klub", + "club.noAccess" to "Nie masz jeszcze dostępu do tego klubu.", + "club.openAccessRequests" to "Otwarte prośby o dostęp", + "club.openRequests" to "Otwarte prośby o dostęp", + "club.requestAccess" to "Poproś o dostęp", + "club.select" to "Wybierz klub", + "club.selectPlaceholder" to "Wybierz klub...", + "club.title" to "Klub", + "club.trainingDiary" to "Dziennik treningowy", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Akcje", + "common.active" to "Aktywny", + "common.add" to "Dodaj", + "common.all" to "Wszystkie", + "common.apply" to "Zastosuj", + "common.back" to "Wstecz", + "common.cancel" to "Anuluj", + "common.choose" to "Wybierz", + "common.clear" to "Wyczyść", + "common.close" to "Zamknij", + "common.confirm" to "Potwierdź", + "common.create" to "Utwórz", + "common.date" to "Data", + "common.days" to "dni", + "common.delete" to "Usuń", + "common.description" to "Opis", + "common.details" to "Szczegóły", + "common.disabled" to "Wyłączony", + "common.download" to "Pobierz", + "common.edit" to "Edytuj", + "common.enabled" to "Włączony", + "common.filter" to "Filtruj", + "common.hours" to "godziny", + "common.in" to "w", + "common.inactive" to "Nieaktywny", + "common.loading" to "Ładowanie...", + "common.min" to "min", + "common.minutes" to "minuty", + "common.months" to "miesiące", + "common.move" to "Przenieś", + "common.name" to "Nazwa", + "common.new" to "Nowy", + "common.next" to "Dalej", + "common.no" to "Nie", + "common.ok" to "OK", + "common.optional" to "Opcjonalne", + "common.period" to "Okres", + "common.previous" to "Poprzedni", + "common.refresh" to "Odśwież", + "common.remove" to "Usuń", + "common.required" to "Wymagane", + "common.reset" to "Resetuj", + "common.save" to "Zapisz", + "common.saved" to "Zapisano", + "common.saving" to "Zapisywanie...", + "common.search" to "Szukaj", + "common.select" to "Wybierz", + "common.status" to "Status", + "common.submit" to "Wyślij", + "common.time" to "Czas", + "common.today" to "Dzisiaj", + "common.type" to "Typ", + "common.update" to "Aktualizuj", + "common.view" to "Pokaż", + "common.weeks" to "tygodnie", + "common.years" to "lata", + "common.yes" to "Tak", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Dodaj zagranie", + "courtDrawingTool.backhand" to "Backhand", + "courtDrawingTool.codeLabel" to "Kod", + "courtDrawingTool.completeFirstStroke" to "Uzupełnij pierwsze zagranie", + "courtDrawingTool.completeFirstStrokeHint" to "Wybierz pozycję startową, stronę, rotację i cel. Następnie pojawi się grafika.", + "courtDrawingTool.configureExercise" to "Konfiguruj ćwiczenie", + "courtDrawingTool.counterSpin" to "Kontratopspin", + "courtDrawingTool.forehand" to "Forehand", + "courtDrawingTool.noAdditionalStrokes" to "Nie dodano jeszcze kolejnych zagrań.", + "courtDrawingTool.preview" to "Podgląd", + "courtDrawingTool.previewHint" to "Grafika pojawi się, gdy pierwsze zagranie będzie w pełni skonfigurowane.", + "courtDrawingTool.service" to "Serwis:", + "courtDrawingTool.serviceTitle" to "Serwis", + "courtDrawingTool.sidespin" to "Rotacja boczna", + "courtDrawingTool.sideUnderspin" to "Boczne podcięcie", + "courtDrawingTool.spin" to "Rotacja:", + "courtDrawingTool.startLeft" to "lewa", + "courtDrawingTool.startMiddle" to "środek", + "courtDrawingTool.startRight" to "prawa", + "courtDrawingTool.stepAdditionalStrokes" to "4. Kolejne zagrania", + "courtDrawingTool.stepAdditionalStrokesHint" to "Opcjonalnie dodaj kolejne piłki jako sekwencję.", + "courtDrawingTool.stepFirstStroke" to "2. Pierwsze zagranie", + "courtDrawingTool.stepFirstStrokeHint" to "Ustaw stronę i rotację pierwszej piłki.", + "courtDrawingTool.stepStartPosition" to "1. Pozycja startowa", + "courtDrawingTool.stepStartPositionHint" to "Z której pozycji serwisowej zaczyna się ćwiczenie?", + "courtDrawingTool.stepTarget" to "3. Cel", + "courtDrawingTool.stepTargetHint" to "Wybierz strefę celu dla pierwszego zagrania.", + "courtDrawingTool.strokeSide" to "Strona", + "courtDrawingTool.strokeTypeBlock" to "Blok", + "courtDrawingTool.strokeTypeChopDefense" to "Obrona podcięciem", + "courtDrawingTool.strokeTypeCounter" to "Kontra", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Typ zagrania", + "courtDrawingTool.strokeTypeLobDefense" to "Obrona lobem", + "courtDrawingTool.strokeTypePush" to "Pchnięcie", + "courtDrawingTool.strokeTypeSmash" to "Smecz", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "Backhand półdługi", + "courtDrawingTool.targetBackhandLong" to "Backhand długi", + "courtDrawingTool.targetBackhandShort" to "Backhand krótki", + "courtDrawingTool.targetForehandHalfLong" to "Forehand półdługi", + "courtDrawingTool.targetForehandLong" to "Forehand długi", + "courtDrawingTool.targetForehandShort" to "Forehand krótki", + "courtDrawingTool.targetMiddleHalfLong" to "Środek półdługi", + "courtDrawingTool.targetMiddleLong" to "Środek długi", + "courtDrawingTool.targetMiddleShort" to "Środek krótki", + "courtDrawingTool.targetPosition" to "Pozycja celu:", + "courtDrawingTool.targetPositionLabel" to "Pozycja celu", + "courtDrawingTool.title" to "Rysunek ćwiczenia tenisa stołowego", + "courtDrawingTool.titleLabel" to "Tytuł", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "do", + "courtDrawingTool.underspin" to "Podcięcie", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Aktywny dzień treningowy", + "diary.activities" to "Aktywności", + "diary.activity" to "Aktywność", + "diary.activityDrawing" to "Rysunek aktywności", + "diary.activityImage" to "Obraz aktywności", + "diary.activityNotFound" to "Nie znaleziono aktywności. Wybierz jedną z listy.", + "diary.activityOrTimeblock" to "Aktywność / blok czasowy", + "diary.activityPlaceholder" to "Aktywność", + "diary.activityRequired" to "Wpisz aktywność.", + "diary.addActivity" to "Dodaj aktywność", + "diary.addGroup" to "Dodaj grupę", + "diary.addGroupActivity" to "Dodaj aktywność grupową", + "diary.addGroupButton" to "+ Grupa", + "diary.addTimeblock" to "Blok czasowy", + "diary.all" to "Wszystkie", + "diary.applySuggestion" to "Zastosuj sugestię", + "diary.assignParticipants" to "Przypisz uczestników", + "diary.assignParticipantsForGroupActivity" to "Przypisz uczestników do aktywności grupowej", + "diary.assignShort" to "Przypisz", + "diary.bookAccident" to "Zarejestruj wypadek", + "diary.confirmDelete" to "Potwierdź usunięcie", + "diary.confirmDeleteDate" to "Czy na pewno chcesz usunąć tę datę?", + "diary.confirmDeleteDateDetails" to "Wszystkie powiązane dane również zostaną usunięte.", + "diary.confirmDeleteGroup" to "Czy na pewno chcesz usunąć grupę „{name}”?", + "diary.createDate" to "Utwórz datę", + "diary.createDrawing" to "Utwórz rysunek ćwiczenia", + "diary.createGroups" to "Utwórz grupy", + "diary.createNew" to "Utwórz", + "diary.createNewDate" to "Utwórz nową datę", + "diary.date" to "Data", + "diary.dateCannotBeDeleted" to "Nie można usunąć daty", + "diary.dateCannotBeDeletedDetails" to "Nadal istnieje zawartość (plan, uczestnicy, aktywności, wypadki lub notatki).", + "diary.dateNoLongerCurrent" to "Wybrana data nie była już aktualna. Spróbuj ponownie.", + "diary.delete" to "Usuń", + "diary.deleteDate" to "Usuń datę", + "diary.deleteGroup" to "Usuń grupę", + "diary.duration" to "Czas trwania", + "diary.durationExampleLong" to "np. 2x7 lub 3*5", + "diary.durationExampleShort" to "np. 2x7", + "diary.durationMinutes" to "Czas trwania (min)", + "diary.editActivity" to "Edytuj aktywność", + "diary.editGroupActivity" to "Edytuj aktywność grupową", + "diary.editTrainingTimes" to "Edytuj godziny treningu", + "diary.errorCreatingActivity" to "Błąd podczas tworzenia aktywności", + "diary.errorCreatingGroups" to "Błąd podczas tworzenia grup", + "diary.errorDeletingGroup" to "Błąd podczas usuwania grupy", + "diary.errorLoadingPredefinedActivities" to "Błąd podczas ładowania predefiniowanych aktywności", + "diary.errorMarkingForm" to "Błąd podczas oznaczania formularza", + "diary.errorOccurred" to "Wystąpił błąd. Spróbuj ponownie.", + "diary.existingGroups" to "Istniejące grupy", + "diary.filterAbsent" to "Nieobecny", + "diary.filterAll" to "Wszystkie", + "diary.filterExcused" to "Usprawiedliwiony", + "diary.filterPresent" to "Obecny", + "diary.filterTest" to "Próba", + "diary.formHandedOver" to "Formularz członkowski przekazany", + "diary.formMarkedAsHandedOver" to "Formularz członkowski oznaczono jako przekazany", + "diary.freeActivities" to "Wolne aktywności", + "diary.gallery" to "Galeria członków", + "diary.galleryCreating" to "Tworzenie galerii…", + "diary.group" to "Grupa...", + "diary.groupDeletedSuccessfully" to "Grupa została pomyślnie usunięta.", + "diary.groupManagement" to "Zarządzanie grupami", + "diary.groupsCreated" to "Pomyślnie utworzono {count} grup.", + "diary.groupsLabel" to "Grupy", + "diary.groupsSection" to "Grupy", + "diary.leader" to "Prowadzący", + "diary.min" to "min", + "diary.minutes" to "Minuty", + "diary.mustCreateAtLeastTwoGroups" to "Za pierwszym razem trzeba utworzyć co najmniej 2 grupy.", + "diary.nextAppointment" to "Następny termin", + "diary.noActiveTrainingDay" to "Nie wybrano dnia treningowego.", + "diary.noEntries" to "Brak wpisów", + "diary.noFreeActivitiesYet" to "Nie zapisano jeszcze żadnych wolnych aktywności.", + "diary.noParticipants" to "Brak uczestników dla tego dnia treningowego.", + "diary.numberOfGroups" to "Liczba grup", + "diary.oneGroupAdded" to "Pomyślnie dodano 1 grupę.", + "diary.openPlanItems" to "{count} otwarte", + "diary.openPlanItemsLabel" to "Status planu", + "diary.overallActivity" to "Aktywność ogólna", + "diary.participants" to "Uczestnicy", + "diary.participantStatusCancelled" to "Anulowano", + "diary.participantStatusExcused" to "Usprawiedliwiony", + "diary.participantStatusNone" to "Brak statusu", + "diary.planActivitiesCount" to "Zaplanowane aktywności", + "diary.planAddHint" to "Dodawaj nowe elementy planu za pomocą powyższych akcji.", + "diary.planEmptyState" to "W planie treningowym nic jeszcze nie wpisano.", + "diary.quickAdd" to "+ Szybkie dodawanie", + "diary.searchParticipants" to "Szukaj uczestników", + "diary.selectGroup" to "Wybierz grupę...", + "diary.selectGroupAndActivity" to "Wybierz grupę i wpisz aktywność.", + "diary.selectParticipantAndNote" to "Wybierz uczestnika i wpisz notatkę.", + "diary.selectTags" to "Wybierz tagi", + "diary.selectTrainingGroup" to "Wybierz grupę treningową", + "diary.selectTrainingGroupPlaceholder" to "Wybierz...", + "diary.showImage" to "Pokaż obraz/rysunek", + "diary.skipSuggestion" to "Kontynuuj bez sugestii", + "diary.standardActivities" to "Aktywności standardowe", + "diary.standardActivityAddError" to "Nie udało się dodać standardowej aktywności.", + "diary.standardDurationShort" to "min", + "diary.startTime" to "Godzina rozpoczęcia", + "diary.statusEmpty" to "Ten dzień treningowy jest jeszcze pusty.", + "diary.statusInProgress" to "Ten dzień treningowy jest częściowo przygotowany.", + "diary.statusOpenShort" to "Otwarte", + "diary.statusReady" to "Godziny i plan treningowy są ustawione.", + "diary.statusReadyShort" to "Gotowe", + "diary.suggestion" to "Sugestia", + "diary.timeblock" to "Blok czasowy", + "diary.timeblocksCount" to "Bloki czasowe", + "diary.title" to "Dziennik treningowy", + "diary.today" to "Dzisiaj", + "diary.trainingDayAsPDF" to "Pobierz dzień treningowy jako PDF", + "diary.trainingDayAsPDFShort" to "Dzień treningowy jako PDF", + "diary.trainingDayChecklist" to "Lista końcowa", + "diary.trainingDaySection" to "Dzień treningowy", + "diary.trainingDaySummaryPdfShort" to "Podsumowanie uczestników jako PDF", + "diary.trainingEnd" to "Koniec treningu", + "diary.trainingPlan" to "Plan treningowy", + "diary.trainingPlanAsPDF" to "Plan treningowy jako PDF", + "diary.trainingPlanPdfShort" to "Plan jako PDF", + "diary.trainingStart" to "Początek treningu", + "diary.trainingTimesUpdated" to "Godziny treningu zostały pomyślnie zaktualizowane.", + "diary.trainingWindow" to "Okno treningowe", + "diary.trainingWindowUnset" to "Jeszcze nie ustawiono", + "diary.unassignedPlanItems" to "{count} otwarte", + "diary.updateTimes" to "Aktualizuj godziny", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Niemiecki)", + "languages.de-CH" to "Schwiizerdütsch (Niemiecki szwajcarski)", + "languages.en-AU" to "English (Angielski (AU))", + "languages.en-GB" to "English (Angielski (GB))", + "languages.en-US" to "English (Angielski (US))", + "languages.es" to "Español (Hiszpański)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (Francuski)", + "languages.it" to "Italiano (Włoski)", + "languages.ja" to "日本語 (Japoński)", + "languages.pl" to "Polski", + "languages.th" to "ไทย (Tajski)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Chiński)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Akcje", + "members.active" to "Aktywny", + "members.activeMembers" to "Aktywni członkowie", + "members.addEmail" to "Dodaj adres e-mail", + "members.addGroup" to "Dodaj grupę...", + "members.addPhone" to "Dodaj numer telefonu", + "members.address" to "Adres", + "members.adultReleaseApproved" to "Dopuszczony do dorosłych", + "members.adultReserveApproved" to "Rezerwa dorosłych", + "members.adults" to "Dorośli (20+)", + "members.age" to "Wiek", + "members.ageFromPlaceholder" to "od", + "members.ageGroup" to "Kategoria wiekowa", + "members.ageRange" to "Wiek od - do", + "members.ageToPlaceholder" to "do", + "members.batchFormsMarkedEmpty" to "Brak niezweryfikowanych formularzy w bieżącym zaznaczeniu.", + "members.batchFormsMarkedSuccess" to "Oznaczono {count} formularz(y) jako zweryfikowane.", + "members.batchMarkedRegularEmpty" to "Brak członków próbnych w bieżącym zaznaczeniu.", + "members.batchMarkedRegularSuccess" to "Oznaczono {count} członka/ów próbnych jako regularnych.", + "members.batchPartialFailure" to "{success} zakończonych sukcesem, {failed} nieudanych.", + "members.birthdate" to "Data urodzenia", + "members.bulkActions" to "Akcje zbiorcze", + "members.camera" to "Aparat", + "members.change" to "Zmień", + "members.city" to "Miasto", + "members.clearFields" to "Wyczyść pola", + "members.clearFilters" to "Resetuj filtry", + "members.clickTtRequestAction" to "Poproś o uprawnienie Click-TT", + "members.clickTtRequestConfirm" to "Czy uruchomić automatyczny wniosek Click-TT dla {name}?", + "members.clickTtRequestError" to "Nie udało się wysłać wniosku Click-TT.", + "members.clickTtRequestFailedLog" to "Wniosek Click-TT nie powiódł się", + "members.clickTtRequestHint" to "Wniosek jest przetwarzany automatycznie przez backendowy workflow Click-TT.", + "members.clickTtRequestPending" to "Wniosek Click-TT w toku", + "members.clickTtRequestPendingShort" to "W toku...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Zaloguj się jeszcze do click-tt i wyślij tam wniosek.", + "members.clickTtRequestTitle" to "Uruchom wniosek Click-TT", + "members.clickTtSubmitted" to "Click-TT wysłane", + "members.closeEditor" to "Zamknij edytor", + "members.composeEmail" to "Przygotuj e-mail", + "members.contact" to "Kontakt", + "members.copyContactSummary" to "Kopiuj podsumowanie kontaktów", + "members.copyContactSummarySuccess" to "Podsumowanie kontaktów skopiowano do schowka.", + "members.copyEmails" to "Kopiuj e-maile", + "members.copyEmailsEmpty" to "Brak adresów e-mail w bieżącym zaznaczeniu.", + "members.copyEmailsSuccess" to "Lista e-mail skopiowana do schowka.", + "members.copyPhones" to "Kopiuj telefony", + "members.copyPhonesEmpty" to "Brak numerów telefonu w bieżącym zaznaczeniu.", + "members.copyPhonesSuccess" to "Lista telefoniczna skopiowana do schowka.", + "members.create" to "Utwórz", + "members.createNewMember" to "Utwórz nowego członka", + "members.dataIssueAddress" to "Brak adresu", + "members.dataIssueBirthdate" to "Brak daty urodzenia", + "members.dataIssueCity" to "Brak miasta", + "members.dataIssueEmail" to "Brak e-maila", + "members.dataIssueGender" to "Płeć nieokreślona", + "members.dataIssuePhone" to "Brak telefonu", + "members.dataIssuePostalCode" to "Brak kodu pocztowego", + "members.dataIssueStreet" to "Brak ulicy", + "members.dataIssueTrainingGroup" to "Brak grupy treningowej", + "members.dataQuality" to "Jakość danych", + "members.dataQualityComplete" to "Dane kompletne", + "members.deactivateMember" to "Dezaktywuj członka", + "members.deactivateMemberConfirm" to "Czy na pewno chcesz dezaktywować „{name}”?", + "members.deactivateMemberTitle" to "Dezaktywuj członka", + "members.deleteImageConfirm" to "Czy na pewno chcesz usunąć to zdjęcie?", + "members.deleteImageTitle" to "Usuń zdjęcie", + "members.editHint" to "Kliknięcie wiersza otwiera edytor.", + "members.editMember" to "Edytuj członka", + "members.editorAssignTrainingGroupHint" to "Przypisz co najmniej jedną grupę treningową.", + "members.editorCreateHint" to "Utwórz nowego członka i od razu wprowadź dane kontaktowe.", + "members.editorEditHint" to "Edytuj dane użytkownika {name}.", + "members.editorRecommendedEntry" to "Zalecany wpis", + "members.emailAddress" to "Adres e-mail", + "members.emailAddressShort" to "E-mail", + "members.emails" to "Adresy e-mail", + "members.errorAddingToGroup" to "Błąd podczas dodawania do grupy", + "members.errorDeactivatingMember" to "Błąd podczas dezaktywacji członka", + "members.errorDeletingImage" to "Nie udało się usunąć zdjęcia", + "members.errorLoadingImage" to "Błąd podczas ładowania zdjęcia", + "members.errorLoadingMembers" to "Nie udało się załadować listy członków.", + "members.errorLoadingTrainingParticipations" to "Błąd podczas ładowania udziałów treningowych", + "members.errorMarkingForm" to "Błąd podczas oznaczania formularza.", + "members.errorRemovingFromGroup" to "Błąd podczas usuwania z grupy", + "members.errorRemovingTestMembership" to "Błąd podczas usuwania członkostwa próbnego.", + "members.errorRotatingImage" to "Błąd podczas obracania zdjęcia", + "members.errorSavingMember" to "Błąd podczas zapisywania członka", + "members.errorSettingPrimaryImage" to "Nie udało się ustawić głównego zdjęcia", + "members.errorTransfer" to "Błąd transferu", + "members.errorUpdatingRatings" to "Błąd podczas aktualizacji wartości TTR/QTTR", + "members.errorUploadingImage" to "Nie udało się przesłać zdjęcia", + "members.exercises" to "Ćwiczenia", + "members.exportCsv" to "Eksportuj CSV", + "members.exportCsvFile" to "wybor-czlonkow.csv", + "members.exportEmails" to "E-mail", + "members.exportMembersSelected" to "aktualnie wybrani członkowie", + "members.exportPhones" to "Telefon", + "members.exportPreview" to "Podgląd eksportu", + "members.exportPreviewEmpty" to "Brak członków w bieżącym zaznaczeniu", + "members.exportPreviewNames" to "Podgląd", + "members.exportReachableByEmail" to "z adresem e-mail", + "members.exportReachableByPhone" to "z numerem telefonu", + "members.firstName" to "Imię", + "members.formHandedOver" to "Formularz członkowski przekazany", + "members.formMarkedAsHandedOver" to "Formularz członkowski oznaczono jako przekazany.", + "members.gender" to "Płeć", + "members.genderDiverse" to "Inna", + "members.genderFemale" to "Żeńska", + "members.genderMale" to "Męska", + "members.genderUnknown" to "Nieznana", + "members.generatePhoneList" to "Generuj listę telefoniczną", + "members.groupPhotoCrop" to "Przetwórz zdjęcie grupowe", + "members.groupPhotoCropSaved" to "Zdjęcie członka zapisane ze zdjęcia grupowego.", + "members.image" to "Obraz", + "members.imageDeleted" to "Zdjęcie zostało usunięte.", + "members.imageInternet" to "Zdjęcie (www?)", + "members.imagePreview" to "Podgląd zdjęcia członka", + "members.imageUpdated" to "Zdjęcie zaktualizowano", + "members.inactive" to "nieaktywny", + "members.inactiveMembers" to "Nieaktywni członkowie", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "U17 (17 lat i mniej)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Nazwisko", + "members.lastTrainingFilter" to "Ostatni trening", + "members.lastTrainingFilterHasDate" to "Z zapisaną datą", + "members.lastTrainingFilterHint" to "Tabela: bieżący sezon w kolumnie AK. Pełne szczegóły (oba sezony, ostatni trening, udziały) po najechaniu na wiersz.", + "members.lastTrainingFilterNoDate" to "Bez ostatniego treningu", + "members.lastTrainingFilterNotInTraining" to "„Nie trenuje już”", + "members.loadingMembers" to "Ładowanie członków...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Oznacz formularz jako zweryfikowany", + "members.markFormsForSelection" to "Zweryfikuj formularze zaznaczenia", + "members.markRegular" to "Oznacz jako regularny", + "members.markRegularForSelection" to "Oznacz zaznaczenie jako regularne", + "members.memberDeactivated" to "Członek został dezaktywowany", + "members.memberDetails" to "Szczegóły członka", + "members.memberFormHandedOver" to "Formularz członkowski przekazany", + "members.memberImage" to "Zdjęcie członka", + "members.memberImages" to "Zdjęcia członka", + "members.memberInfo" to "Informacje o członku", + "members.memberScope" to "Zakres członków", + "members.missingTtrHistoryId" to "Brak zapisanego identyfikatora myTischtennis", + "members.name" to "Nazwisko, imię", + "members.newMember" to "Nowy członek", + "members.noAddressShort" to "Brak adresu", + "members.noEmailShort" to "Brak e-maila", + "members.noGroupsAssigned" to "Brak przypisanych grup", + "members.noGroupsAvailable" to "Brak dostępnych grup", + "members.noOpenTasks" to "Brak otwartych zadań", + "members.noPhoneShort" to "Brak telefonu", + "members.notes" to "Notatki", + "members.noTestMembership" to "Nie jest już członkiem próbnym", + "members.notInTrainingTooltip" to "Brak udziału od {weeks} tygodni treningowych", + "members.onlyActiveMembers" to "Uwzględniani są tylko aktywni członkowie", + "members.openTasks" to "Otwarte zadania", + "members.parent" to "Rodzic", + "members.parentFallback" to "Rodzic", + "members.parentName" to "Imię (np. matka, ojciec)", + "members.phoneList" to "lista-telefoniczna.pdf", + "members.phoneListForSelection" to "Lista telefoniczna dla zaznaczenia", + "members.phoneListSelectionFile" to "lista-telefoniczna-zaznaczenie.pdf", + "members.phoneNumber" to "Numer telefonu", + "members.phoneNumberShort" to "Tel.", + "members.phones" to "Numery telefonów", + "members.picsInInternetAllowed" to "Zdjęcia dozwolone w internecie", + "members.postalCode" to "Kod pocztowy", + "members.previewLastTraining" to "Ostatni trening", + "members.previewNoLastTraining" to "Brak udziału do tej pory", + "members.primary" to "Główny", + "members.primaryImageUpdated" to "Zaktualizowano główne zdjęcie.", + "members.remove" to "Usuń", + "members.resultsVisible" to "widocznych członków", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Aktywni", + "members.scopeActiveDataIncomplete" to "Aktywny + niepełne dane", + "members.scopeActiveTest" to "Aktywni + próba", + "members.scopeAll" to "Wszyscy", + "members.scopeDataIncomplete" to "Niepełne dane", + "members.scopeInactive" to "Nieaktywni", + "members.scopeNeedsForm" to "Formularz niezweryfikowany", + "members.scopeNotTraining" to "Już nie trenuje", + "members.scopeTest" to "Próba", + "members.search" to "Szukaj", + "members.searchAndFilter" to "Wyszukiwanie i filtry", + "members.searchPlaceholder" to "Szukaj po nazwisku, mieście, telefonie lub e-mailu", + "members.selectFile" to "Wybierz plik", + "members.showInactiveMembers" to "Pokaż nieaktywnych członków", + "members.showTrainingParticipationsColumn" to "Pokaż kolumnę „Udziały w treningu”", + "members.showTtrHistory" to "Pokaż historię TTR", + "members.sixOrMoreParticipations" to "6 lub więcej udziałów w treningu", + "members.sortAge" to "Wiek", + "members.sortBirthday" to "Data urodzenia", + "members.sortBy" to "Sortuj według", + "members.sortFirstName" to "Imię", + "members.sortLastName" to "Nazwisko", + "members.sortLastTraining" to "Ostatni trening", + "members.sortOpenTasks" to "Otwarte zadania", + "members.sortQttr" to "Wartość QTTR", + "members.status" to "Status", + "members.street" to "Ulica", + "members.subtitle" to "Szukaj, filtruj i edytuj członków bezpośrednio.", + "members.taskActionMarkRegular" to "Oznacz jako regularny", + "members.taskActionRequest" to "Poproś", + "members.taskActionReview" to "Otwórz", + "members.taskActionVerify" to "Zweryfikuj", + "members.taskAssignTrainingGroup" to "Przypisz grupę treningową", + "members.taskCheckClickTt" to "Sprawdź uprawnienie Click-TT", + "members.taskCheckDataQuality" to "Sprawdź jakość danych", + "members.taskCheckTrainingStatus" to "Sprawdź status treningowy", + "members.taskReviewTrialStatus" to "Sprawdź status próbny", + "members.taskVerifyForm" to "Zweryfikuj formularz", + "members.testMember" to "Próba", + "members.testMembers" to "Członkowie próbni", + "members.testMembership" to "Członkostwo próbne", + "members.testMembershipRemoved" to "Usunięto członkostwo próbne.", + "members.threeOrMoreParticipations" to "3 lub więcej udziałów w treningu", + "members.title" to "Członkowie", + "members.toggleSortDirection" to "Zmień kierunek sortowania", + "members.trainingGroups" to "Grupy treningowe", + "members.trainingParticipations" to "Udziały w treningach", + "members.transferErrorTitle" to "Błąd transferu", + "members.transferMembers" to "Przenieś członków", + "members.transferSuccessTitle" to "Transfer zakończony sukcesem", + "members.ttAdult" to "Dorośli (brak juniora wg terminu)", + "members.ttAgeClassCol" to "Kategoria (TT)", + "members.ttFilterGroupJ" to "Dziewczęta i chłopcy (mieszane)", + "members.ttFilterGroupM" to "Tylko dziewczęta", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "bieżący", + "members.ttSeasonFilter" to "Sezon (termin)", + "members.ttSeasonNextTag" to "następny", + "members.ttStichtagHint" to "Termin 1.01 (DTTB). Chłopcy: tylko klasy J. Dziewczęta: J i M.", + "members.updateRatings" to "Aktualizuj TTR/QTTR z myTischtennis", + "members.updating" to "Aktualizowanie...", + "members.visibleMembers" to "Widoczni członkowie", + "members.wantsToPlay" to "Chce grać", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Anuluj", + "messages.confirm" to "Potwierdź", + "messages.error" to "Błąd", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Informacja", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Sukces", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Ostrzeżenie", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Zatwierdzenia", + "navigation.backToHome" to "Powrót do strony głównej", + "navigation.billing" to "Rozliczenia", + "navigation.clickTtAccount" to "Konto HTTV / click-TT", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Ustawienia klubu", + "navigation.clubTournaments" to "Turnieje klubowe", + "navigation.competitions" to "Rozgrywki", + "navigation.dailyBusiness" to "Codzienna praca", + "navigation.diary" to "Dziennik", + "navigation.home" to "Strona główna", + "navigation.login" to "Zaloguj", + "navigation.logout" to "Wyloguj", + "navigation.logs" to "Logi systemowe", + "navigation.members" to "Członkowie", + "navigation.memberTransfer" to "Transfer członków", + "navigation.myTischtennisAccount" to "Konto myTischtennis", + "navigation.orders" to "Zamówienia", + "navigation.permissions" to "Uprawnienia", + "navigation.personalSettings" to "Ustawienia osobiste", + "navigation.predefinedActivities" to "Predefiniowane aktywności", + "navigation.register" to "Zarejestruj", + "navigation.schedule" to "Harmonogramy", + "navigation.settings" to "Ustawienia", + "navigation.statistics" to "Statystyki treningowe", + "navigation.teamManagement" to "Zarządzanie drużyną", + "navigation.tournamentParticipations" to "Udziały w turniejach", + "navigation.tournaments" to "Turnieje", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Dodaj zamówienie", + "orders.budget" to "Budget", + "orders.club" to "Klub", + "orders.cost" to "Koszt", + "orders.dateAutoHint" to "Data jest ustawiana automatycznie, a każda zmiana jest zapisywana z datą.", + "orders.errorLoading" to "Nie udało się załadować zamówień.", + "orders.errorSaving" to "Nie udało się zapisać zamówienia.", + "orders.filterAllClubs" to "Wszystkie kluby", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Wszystkie statusy", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Tutaj można przeglądać i zarządzać zamówieniami ze wszystkich klubów.", + "orders.globalTitle" to "Zamówienia ze wszystkich klubów", + "orders.history" to "Historia", + "orders.item" to "Artykuł", + "orders.itemPlaceholder" to "np. koszulka, bluza lub pokrowiec na rakietkę", + "orders.loading" to "Ładowanie zamówień...", + "orders.member" to "Członek", + "orders.memberTitle" to "Zamówienia: {name}", + "orders.noOrdersGlobal" to "Obecnie nie ma żadnych zamówień.", + "orders.noOrdersMember" to "Ten członek nie ma jeszcze żadnych zamówień.", + "orders.open" to "Pozostało", + "orders.orderDate" to "Utworzono", + "orders.paid" to "Zapłacono", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Szukaj po klubie, członku lub artykule", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "artykuł dotarł", + "orders.statusDate" to "Ostatnia zmiana", + "orders.statusHandedOver" to "artykuł wydany", + "orders.statusOrdered" to "zamówione", + "orders.statusRequested" to "pożądane", + "orders.title" to "Zamówienia", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Klub:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Utworzono:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Liga:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Zgłoszenie na:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Planowana liga:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Sezon:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Kategoria wiekowa drużyny:", + "pdfGenerator.teamGenderLabel" to "Płeć drużyny:", + "pdfGenerator.teamLineupTitle" to "Skład drużyny", + "pdfGenerator.teamNameLabel" to "Drużyna:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Język", + "settings.languageChanged" to "Język został zmieniony pomyślnie", + "settings.languageDescription" to "Wybierz preferowany język aplikacji", + "settings.personalSettings" to "Ustawienia osobiste", + "settings.selectLanguage" to "Wybierz język", + "settings.title" to "Ustawienia", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Dostępni zawodnicy", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Uprawnienie", + "teamManagement.eligibilityAdultRelease" to "Dopuszczony do dorosłych", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Dopuszczony + rezerwowy u dorosłych", + "teamManagement.eligibilityAdultReserve" to "Rezerwowy u dorosłych", + "teamManagement.eligibilityRegular" to "Standardowe", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "Eksport składu do PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Pierwsza runda", + "teamManagement.firstHalfFull" to "Pierwsza runda (lipiec - grudzień)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Wybierz klasę rozgrywkową i zapisz.", + "teamManagement.lineupEmpty" to "Do tej drużyny nie zgłoszono jeszcze żadnych zawodników.", + "teamManagement.lineupPdfEmpty" to "Brak zawodników w składzie – nie można utworzyć PDF.", + "teamManagement.lineupPdfFilePrefix" to "Sklad", + "teamManagement.lineupProposal" to "Zgłoszenie według QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Nie udało się zapisać zgłoszenia drużyny.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} ma ponad 30 punktów QTTR przewagi nad {lower}. Popraw tę kolejność.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Brakujące informacje:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "Wklej i przeanalizuj adres URL MyTischtennis, aby powiązać ID drużyny i dane ligi.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Planowana klasa rozgrywkowa", + "teamManagement.plannedLeagueHint" to "Opcjonalnie. Niezależnie od zgłoszonej ligi (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "np. klasa okręgowa, po następnej deklaracji …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Druga runda", + "teamManagement.secondHalfFull" to "Druga runda (od 1 stycznia)", + "teamManagement.selectedLineup" to "Zgłoszeni zawodnicy", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Dodaj regułę puli", + "tournaments.address" to "Adres", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Wszystkie dane uczestników są kompletne.", + "tournaments.allDataCompleteTop3" to "Wszystkie dane pierwszych 3 miejsc są kompletne.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Dodaj co najmniej jedną regułę puli dla {label} (np. miejsca 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Popraw", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Utwórz rundę finałową z rundy pośredniej", + "tournaments.createFinalFromPreliminary" to "Utwórz rundę finałową z rundy wstępnej", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Utwórz rundę pośrednią z rundy wstępnej", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Jeszcze nie wprowadzono", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Czy na pewno chcesz usunąć klasę „{name}”?", + "tournaments.deleteClassParticipantsDetached" to "Wszyscy uczestnicy zostaną odłączeni od tej klasy.", + "tournaments.deleteClassTitle" to "Usuń klasę", + "tournaments.deleteExistingPairingsConfirm" to "Istniejące pary zostaną usunięte. Kontynuować?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Rozdziel wolne stoły", + "tournaments.distributeTablesResult" to "Podział stołów", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Pasuje do: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Wprowadź nazwę klasy.", + "tournaments.enterExternalParticipantName" to "Wprowadź co najmniej imię i nazwisko.", + "tournaments.enterMiniLocation" to "Wprowadź lokalizację.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "Błąd podczas rozdzielania stołów.", + "tournaments.errorGeneratingPdf" to "Błąd podczas generowania PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Runda finałowa", + "tournaments.finishMatch" to "Zakończ", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Generowanie PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Grupy", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Runda pośrednia (runda 2)", + "tournaments.internalStatsAbsoluteRank" to "Ranking sumy", + "tournaments.internalStatsAgeFilter" to "Grupa wiekowa i płeć (singel)", + "tournaments.internalStatsAgeFilterAll" to "Wszystkie klasy wiekowe", + "tournaments.internalStatsAgeFilterNone" to "Nie wybrano klasy wiekowej", + "tournaments.internalStatsAgeNoClass" to "Bez przypisania do klasy", + "tournaments.internalStatsAgeSelectAll" to "Wszystkie", + "tournaments.internalStatsAgeSelectNone" to "Żadna", + "tournaments.internalStatsAverageRank" to "Ranking średniej (na turniej)", + "tournaments.internalStatsAvgPoints" to "Śr.", + "tournaments.internalStatsEmpty" to "Brak danych w wybranym okresie.", + "tournaments.internalStatsExportPdf" to "Eksportuj do PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Ostatnie 12 miesięcy", + "tournaments.internalStatsLast3Months" to "Ostatnie 3 miesiące", + "tournaments.internalStatsLast6Months" to "Ostatnie 6 miesięcy", + "tournaments.internalStatsOpenButton" to "Statystyki turniejów (singel)", + "tournaments.internalStatsPeriod" to "Okres", + "tournaments.internalStatsPoints" to "Suma", + "tournaments.internalStatsPointsExplain" to "Punktacja: w każdej grupie miejsce jest wyrażone w procentach (przy N sklasyfikowanych: 1. = 100%, ostatni = 0%, liniowo pomiędzy; remis = ten sam wynik). N liczy wszystkich z miejscem w grupie (goście włączenie). Przy jednym zawodniku: 100%. W KO: maks. wynik grupowy klasy + 1, potem +1 za wygrany mecz KO. Tylko członkowie klubu, singel.", + "tournaments.internalStatsTitle" to "Statystyki turniejów wewnętrznych (singel)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} turniej(ów) w okresie (bez mini-mistrzostw).", + "tournaments.internalStatsTtAdult" to "Dorośli", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Oznacz jako na żywo", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Do parowań wymaganych jest co najmniej 2 uczestników.", + "tournaments.missingDataPDF" to "Brakujące dane jako PDF", + "tournaments.missingDataPDFSubtitle" to "Proszę zebrać brakujące dane (oznaczone ____) od uczestników i zanotować je tutaj.", + "tournaments.missingDataPDFSubtitleTop3" to "Proszę zebrać brakujące dane pierwszych 3 (oznaczone ____) i zanotować je tutaj.", + "tournaments.missingDataPDFTitle" to "Brakujące dane uczestników – Mini mistrzostwa", + "tournaments.missingDataPDFTitleTop3" to "Brakujące dane – Top 3 Mini mistrzostwa", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Nowy set, np. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Brak meczów, w których obaj gracze są wolni.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "Nie znaleziono odpowiedniej klasy.", + "tournaments.noFreeTables" to "Brak wolnych stołów.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Nie znaleziono osieroconych meczów.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Brak reguł. Przykład: miejsca 1 i 2 -> górne grupy rundy 2.", + "tournaments.noPoolRulesYetFinal" to "Brak reguł. Przykład: miejsca 1 i 2 -> runda finałowa.", + "tournaments.noTop3Yet" to "Pierwsze 3 miejsca nie zostały jeszcze ustalone.", + "tournaments.noTournamentDate" to "Brak daty turnieju.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Nie znaleziono treningu dla {date}.", + "tournaments.noTrainingParticipants" to "Nie znaleziono uczestników treningu dla tej daty.", + "tournaments.noValidTrainingParticipants" to "Nie znaleziono prawidłowych uczestników treningu dla tej daty.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Liczba stołów", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "Usunięto {count} osieroconych meczów.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Strona", + "tournaments.pairingAlreadyExists" to "Ta para już istnieje.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "Utworzono {successCount} par, błędy: {errorCount}.", + "tournaments.participantConflicts" to "Konflikty", + "tournaments.participantNotFound" to "Nie znaleziono uczestnika.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Telefon", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Miejsca z każdej grupy (np. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Zawodnik 1", + "tournaments.playerTwo" to "Zawodnik 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Rozegraj mecz o trzecie miejsce", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Awans: runda wstępna → runda pośrednia (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Awans: runda {from} → runda {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Ranking", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Liczba grup", + "tournaments.roundMode" to "Tryb", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "Zapisz rundy", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Najpierw wybierz turniej.", + "tournaments.selectTwoDifferentPlayers" to "Wybierz dwóch różnych zawodników.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Nieprawidłowa odpowiedź serwera.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "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.", + "tournaments.stageConfigLoadError" to "Błąd podczas ładowania konfiguracji rund.", + "tournaments.stageConfigLoading" to "Ładowanie konfiguracji rund…", + "tournaments.stageConfigMissingIds" to "Nie można zapisać: brak identyfikatora klubu lub turnieju.", + "tournaments.stageConfigTitle" to "Runda pośrednia i finałowa", + "tournaments.stageCreated" to "Utworzono rundę {round}.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Zakończone", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "Na żywo", + "tournaments.statusOpen" to "Otwarte", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Stół", + "tournaments.tablesDistributed" to "Stoły zostały rozdzielone.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Cel", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Docelowa liczba grup", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Kobiety", + "tournaments.tournamentClassGenderOpen" to "Otwarta (wszyscy)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Usuń oznaczenie na żywo", + "tournaments.unpairedDoubles" to "Debel bez partnera", + "tournaments.useIntermediateStage" to "Użyj rundy pośredniej", + "tournaments.warningBirthDateMissing" to "Brakuje daty urodzenia dla tej klasy", + "tournaments.warningClassMissing" to "Nie znaleziono klasy", + "tournaments.warningGenderMismatch" to "Płeć nie pasuje do klasy", + "tournaments.warningGenderMissing" to "Brakuje płci dla tej klasy", + "tournaments.warningMissingPairing" to "Brakuje partnera do debla", + "tournaments.warningTooOldForClass" to "Za stary do tej klasy", + "tournaments.warningTooYoungForClass" to "Za młody do tej klasy", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Akcje", + "trainingStats.activeMembers" to "Aktywni członkowie", + "trainingStats.allTrainingDays" to "Wszystkie dni treningowe", + "trainingStats.attendingMembers" to "Obecni członkowie", + "trainingStats.averageParticipationCurrentMonth" to "Średnia frekwencja (bieżący miesiąc)", + "trainingStats.averageParticipationHalfYear" to "Średnia frekwencja (półrocze)", + "trainingStats.averageParticipationLastMonth" to "Średnia frekwencja (poprzedni miesiąc)", + "trainingStats.averageParticipationQuarter" to "Średnia frekwencja (kwartał)", + "trainingStats.averageParticipationYear" to "Średnia frekwencja (rok)", + "trainingStats.birthdate" to "Data urodzenia", + "trainingStats.date" to "Data", + "trainingStats.lastTraining" to "Ostatni trening", + "trainingStats.memberParticipations" to "Udziały członków", + "trainingStats.name" to "Nazwa", + "trainingStats.noParticipants" to "Brak uczestników", + "trainingStats.participants" to "Uczestnicy", + "trainingStats.participations12Months" to "Udziały (12 miesięcy)", + "trainingStats.participations3Months" to "Udziały (3 miesiące)", + "trainingStats.participationsTotal" to "Udziały (łącznie)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Pokaż szczegóły", + "trainingStats.title" to "Statystyki treningowe", + "trainingStats.trainingDayFilter" to "Dzień treningowy", + "trainingStats.trainingDays" to "Dni treningowe (ostatnie 12 miesięcy)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Dzień tygodnia", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val th: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "สมุดบันทึกการฝึกซ้อม", + "app.title" to "สมุดบันทึกการฝึกซ้อม", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "การเปิดใช้งานล้มเหลว กรุณาตรวจสอบลิงก์", + "auth.confirmPassword" to "ยืนยันรหัสผ่าน", + "auth.email" to "อีเมล", + "auth.forgotPassword" to "ลืมรหัสผ่าน?", + "auth.forgotPasswordDescription" to "กรอกอีเมลของคุณ คุณจะได้รับลิงก์สำหรับรีเซ็ตรหัสผ่าน", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "เข้าสู่ระบบ", + "auth.loginFailed" to "เข้าสู่ระบบล้มเหลว กรุณาตรวจสอบข้อมูลของคุณ", + "auth.loginSuccess" to "เข้าสู่ระบบสำเร็จ", + "auth.logout" to "ออกจากระบบ", + "auth.logoutSuccess" to "ออกจากระบบสำเร็จ", + "auth.newPassword" to "รหัสผ่านใหม่", + "auth.noAccount" to "ยังไม่มีบัญชี?", + "auth.password" to "รหัสผ่าน", + "auth.passwordResetSuccess" to "เปลี่ยนรหัสผ่านสำเร็จ คุณสามารถเข้าสู่ระบบได้แล้ว", + "auth.passwordsDoNotMatch" to "รหัสผ่านไม่ตรงกัน", + "auth.passwordTooShort" to "รหัสผ่านต้องมีอย่างน้อย 6 ตัวอักษร", + "auth.register" to "ลงทะเบียน", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "จดจำฉัน", + "auth.resetEmailSent" to "หากมีบัญชีที่ใช้อีเมลนี้ ลิงก์รีเซ็ตได้ถูกส่งแล้ว กรุณาตรวจสอบกล่องจดหมาย (และสแปม)", + "auth.resetFailed" to "ไม่สามารถเปลี่ยนรหัสผ่านได้ ลิงก์อาจหมดอายุแล้ว", + "auth.resetPassword" to "ตั้งรหัสผ่านใหม่", + "auth.resetRequestFailed" to "คำขอล้มเหลว กรุณาลองอีกครั้ง", + "auth.saveNewPassword" to "บันทึกรหัสผ่าน", + "auth.saving" to "กำลังบันทึก...", + "auth.sending" to "กำลังส่ง...", + "auth.sendResetLink" to "ส่งลิงก์", + "auth.sessionExpired" to "เซสชันของคุณหมดอายุแล้ว คุณจะถูกออกจากระบบ", + "auth.toLogin" to "ไปที่เข้าสู่ระบบ", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "ไม่ได้รับอนุญาตให้เข้าถึงสโมสรนี้", + "club.accessRequested" to "ส่งคำขอเข้าถึงแล้ว", + "club.accessRequestFailed" to "ไม่สามารถส่งคำขอเข้าถึงได้", + "club.accessRequestPending" to "ได้ส่งคำขอเข้าถึงสโมสรนี้แล้ว โปรดรอสักครู่", + "club.create" to "สร้างสโมสร", + "club.createTitle" to "สร้างสโมสร", + "club.diary" to "สมุดบันทึกการฝึกซ้อม", + "club.errorLoadingRequests" to "เกิดข้อผิดพลาดในการโหลดคำขอที่รอดำเนินการ", + "club.load" to "โหลด", + "club.members" to "สมาชิก", + "club.mobileSelectHint" to "โปรดเลือกสโมสรก่อนเพื่อใช้แอปบนสมาร์ทโฟน", + "club.name" to "ชื่อสโมสร", + "club.new" to "สโมสรใหม่", + "club.noAccess" to "คุณยังไม่ได้รับสิทธิ์เข้าถึงสโมสรนี้", + "club.openAccessRequests" to "คำขอเข้าถึงที่รอดำเนินการ", + "club.openRequests" to "คำขอเข้าถึงที่รอดำเนินการ", + "club.requestAccess" to "ขอสิทธิ์เข้าถึง", + "club.select" to "เลือกสโมสร", + "club.selectPlaceholder" to "เลือกสโมสร...", + "club.title" to "สโมสร", + "club.trainingDiary" to "สมุดบันทึกการฝึกซ้อม", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "การดำเนินการ", + "common.active" to "ใช้งานอยู่", + "common.add" to "เพิ่ม", + "common.all" to "ทั้งหมด", + "common.apply" to "นำไปใช้", + "common.back" to "กลับ", + "common.cancel" to "ยกเลิก", + "common.choose" to "เลือก", + "common.clear" to "ล้าง", + "common.close" to "ปิด", + "common.confirm" to "ยืนยัน", + "common.create" to "สร้าง", + "common.date" to "วันที่", + "common.days" to "วัน", + "common.delete" to "ลบ", + "common.description" to "คำอธิบาย", + "common.details" to "รายละเอียด", + "common.disabled" to "ปิดใช้งาน", + "common.download" to "ดาวน์โหลด", + "common.edit" to "แก้ไข", + "common.enabled" to "เปิดใช้งาน", + "common.filter" to "กรอง", + "common.hours" to "ชั่วโมง", + "common.in" to "ใน", + "common.inactive" to "ไม่ใช้งาน", + "common.loading" to "กำลังโหลด...", + "common.min" to "นาที", + "common.minutes" to "นาที", + "common.months" to "เดือน", + "common.move" to "ย้าย", + "common.name" to "ชื่อ", + "common.new" to "ใหม่", + "common.next" to "ถัดไป", + "common.no" to "ไม่", + "common.ok" to "ตกลง", + "common.optional" to "ไม่บังคับ", + "common.period" to "ช่วงเวลา", + "common.previous" to "ก่อนหน้า", + "common.refresh" to "โหลดใหม่", + "common.remove" to "เอาออก", + "common.required" to "จำเป็น", + "common.reset" to "รีเซ็ต", + "common.save" to "บันทึก", + "common.saved" to "บันทึกแล้ว", + "common.saving" to "กำลังบันทึก...", + "common.search" to "ค้นหา", + "common.select" to "เลือก", + "common.status" to "สถานะ", + "common.submit" to "ส่ง", + "common.time" to "เวลา", + "common.today" to "วันนี้", + "common.type" to "ประเภท", + "common.update" to "อัปเดต", + "common.view" to "แสดง", + "common.weeks" to "สัปดาห์", + "common.years" to "ปี", + "common.yes" to "ใช่", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "เพิ่มการตี", + "courtDrawingTool.backhand" to "แบ็กแฮนด์", + "courtDrawingTool.codeLabel" to "รหัสย่อ", + "courtDrawingTool.completeFirstStroke" to "กำหนดลูกแรกให้ครบ", + "courtDrawingTool.completeFirstStrokeHint" to "เลือกตำแหน่งเริ่ม ด้านตี สปิน และเป้าหมาย แล้วภาพจะแสดงขึ้น", + "courtDrawingTool.configureExercise" to "กำหนดค่าแบบฝึก", + "courtDrawingTool.counterSpin" to "สปินสวน", + "courtDrawingTool.forehand" to "โฟร์แฮนด์", + "courtDrawingTool.noAdditionalStrokes" to "ยังไม่มีลูกต่อเนื่อง", + "courtDrawingTool.preview" to "ตัวอย่าง", + "courtDrawingTool.previewHint" to "กราฟิกจะแสดงเมื่อกำหนดลูกแรกครบถ้วนแล้ว", + "courtDrawingTool.service" to "เสิร์ฟ:", + "courtDrawingTool.serviceTitle" to "การเสิร์ฟ", + "courtDrawingTool.sidespin" to "ไซด์สปิน", + "courtDrawingTool.sideUnderspin" to "ไซด์อันเดอร์สปิน", + "courtDrawingTool.spin" to "สปิน:", + "courtDrawingTool.startLeft" to "ซ้าย", + "courtDrawingTool.startMiddle" to "กลาง", + "courtDrawingTool.startRight" to "ขวา", + "courtDrawingTool.stepAdditionalStrokes" to "4. ลูกต่อเนื่อง", + "courtDrawingTool.stepAdditionalStrokesHint" to "สามารถเพิ่มลูกต่อเนื่องเป็นรายการได้ตามต้องการ", + "courtDrawingTool.stepFirstStroke" to "2. ลูกแรก", + "courtDrawingTool.stepFirstStrokeHint" to "กำหนดด้านตีและสปินสำหรับลูกแรก", + "courtDrawingTool.stepStartPosition" to "1. ตำแหน่งเริ่มต้น", + "courtDrawingTool.stepStartPositionHint" to "แบบฝึกเริ่มจากตำแหน่งเสิร์ฟใด?", + "courtDrawingTool.stepTarget" to "3. เป้าหมาย", + "courtDrawingTool.stepTargetHint" to "เลือกโซนเป้าหมายสำหรับลูกแรก", + "courtDrawingTool.strokeSide" to "ด้านตี", + "courtDrawingTool.strokeTypeBlock" to "บล็อก", + "courtDrawingTool.strokeTypeChopDefense" to "รับแบบช็อป", + "courtDrawingTool.strokeTypeCounter" to "เคาน์เตอร์", + "courtDrawingTool.strokeTypeFlip" to "ฟลิก", + "courtDrawingTool.strokeTypeLabel" to "ประเภทการตี", + "courtDrawingTool.strokeTypeLobDefense" to "รับแบบลอบ", + "courtDrawingTool.strokeTypePush" to "ผลัก", + "courtDrawingTool.strokeTypeSmash" to "ตบ", + "courtDrawingTool.strokeTypeTopspin" to "ท็อปสปิน", + "courtDrawingTool.targetBackhandHalfLong" to "แบ็กแฮนด์กึ่งยาว", + "courtDrawingTool.targetBackhandLong" to "แบ็กแฮนด์ยาว", + "courtDrawingTool.targetBackhandShort" to "แบ็กแฮนด์สั้น", + "courtDrawingTool.targetForehandHalfLong" to "โฟร์แฮนด์กึ่งยาว", + "courtDrawingTool.targetForehandLong" to "โฟร์แฮนด์ยาว", + "courtDrawingTool.targetForehandShort" to "โฟร์แฮนด์สั้น", + "courtDrawingTool.targetMiddleHalfLong" to "กลางกึ่งยาว", + "courtDrawingTool.targetMiddleLong" to "กลางยาว", + "courtDrawingTool.targetMiddleShort" to "กลางสั้น", + "courtDrawingTool.targetPosition" to "ตำแหน่งเป้าหมาย:", + "courtDrawingTool.targetPositionLabel" to "ตำแหน่งเป้าหมาย", + "courtDrawingTool.title" to "ภาพวาดแบบฝึกปิงปอง", + "courtDrawingTool.titleLabel" to "ชื่อเรื่อง", + "courtDrawingTool.topspin" to "ท็อปสปิน", + "courtDrawingTool.toTarget" to "ไปยัง", + "courtDrawingTool.underspin" to "อันเดอร์สปิน", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "วันฝึกซ้อมที่ใช้งานอยู่", + "diary.activities" to "กิจกรรม", + "diary.activity" to "กิจกรรม", + "diary.activityDrawing" to "ภาพวาดกิจกรรม", + "diary.activityImage" to "รูปกิจกรรม", + "diary.activityNotFound" to "ไม่พบกิจกรรม กรุณาเลือกจากรายการ", + "diary.activityOrTimeblock" to "กิจกรรม / ช่วงเวลา", + "diary.activityPlaceholder" to "กิจกรรม", + "diary.activityRequired" to "กรุณากรอกกิจกรรม", + "diary.addActivity" to "เพิ่มกิจกรรม", + "diary.addGroup" to "เพิ่มกลุ่ม", + "diary.addGroupActivity" to "เพิ่มกิจกรรมของกลุ่ม", + "diary.addGroupButton" to "+ กลุ่ม", + "diary.addTimeblock" to "ช่วงเวลา", + "diary.all" to "ทั้งหมด", + "diary.applySuggestion" to "ใช้ข้อเสนอแนะ", + "diary.assignParticipants" to "กำหนดผู้เข้าร่วม", + "diary.assignParticipantsForGroupActivity" to "กำหนดผู้เข้าร่วมสำหรับกิจกรรมกลุ่ม", + "diary.assignShort" to "กำหนด", + "diary.bookAccident" to "บันทึกอุบัติเหตุ", + "diary.confirmDelete" to "ยืนยันการลบ", + "diary.confirmDeleteDate" to "คุณต้องการลบวันที่นี้จริงหรือไม่?", + "diary.confirmDeleteDateDetails" to "ข้อมูลที่เกี่ยวข้องทั้งหมดจะถูกลบด้วย", + "diary.confirmDeleteGroup" to "คุณต้องการลบกลุ่ม \"{name}\" จริงหรือไม่?", + "diary.createDate" to "สร้างวันที่", + "diary.createDrawing" to "สร้างภาพฝึกซ้อม", + "diary.createGroups" to "สร้างกลุ่ม", + "diary.createNew" to "สร้างใหม่", + "diary.createNewDate" to "สร้างวันที่ใหม่", + "diary.date" to "วันที่", + "diary.dateCannotBeDeleted" to "ไม่สามารถลบวันที่ได้", + "diary.dateCannotBeDeletedDetails" to "ยังมีเนื้อหาอยู่ (แผนการฝึก ผู้เข้าร่วม กิจกรรม อุบัติเหตุ หรือบันทึก)", + "diary.dateNoLongerCurrent" to "วันที่ที่เลือกไม่ใช่ข้อมูลล่าสุดอีกต่อไป โปรดลองอีกครั้ง", + "diary.delete" to "ลบ", + "diary.deleteDate" to "ลบวันที่", + "diary.deleteGroup" to "ลบกลุ่ม", + "diary.duration" to "ระยะเวลา", + "diary.durationExampleLong" to "เช่น 2x7 หรือ 3*5", + "diary.durationExampleShort" to "เช่น 2x7", + "diary.durationMinutes" to "ระยะเวลา (นาที)", + "diary.editActivity" to "แก้ไขกิจกรรม", + "diary.editGroupActivity" to "แก้ไขกิจกรรมกลุ่ม", + "diary.editTrainingTimes" to "แก้ไขเวลาฝึกซ้อม", + "diary.errorCreatingActivity" to "เกิดข้อผิดพลาดในการสร้างกิจกรรม", + "diary.errorCreatingGroups" to "เกิดข้อผิดพลาดในการสร้างกลุ่ม", + "diary.errorDeletingGroup" to "เกิดข้อผิดพลาดในการลบกลุ่ม", + "diary.errorLoadingPredefinedActivities" to "เกิดข้อผิดพลาดในการโหลดกิจกรรมที่กำหนดไว้ล่วงหน้า", + "diary.errorMarkingForm" to "เกิดข้อผิดพลาดในการทำเครื่องหมายแบบฟอร์มสมาชิก", + "diary.errorOccurred" to "เกิดข้อผิดพลาด โปรดลองอีกครั้ง", + "diary.existingGroups" to "กลุ่มที่มีอยู่", + "diary.filterAbsent" to "ขาด", + "diary.filterAll" to "ทั้งหมด", + "diary.filterExcused" to "ลา", + "diary.filterPresent" to "มาเข้าร่วม", + "diary.filterTest" to "ทดลอง", + "diary.formHandedOver" to "ส่งแบบฟอร์มสมาชิกแล้ว", + "diary.formMarkedAsHandedOver" to "ทำเครื่องหมายว่าได้ส่งแบบฟอร์มสมาชิกแล้ว", + "diary.freeActivities" to "กิจกรรมอิสระ", + "diary.gallery" to "แกลเลอรีสมาชิก", + "diary.galleryCreating" to "กำลังสร้างแกลเลอรี…", + "diary.group" to "กลุ่ม...", + "diary.groupDeletedSuccessfully" to "ลบกลุ่มสำเร็จ", + "diary.groupManagement" to "การจัดการกลุ่ม", + "diary.groupsCreated" to "สร้างกลุ่มสำเร็จ {count} กลุ่ม", + "diary.groupsLabel" to "กลุ่ม", + "diary.groupsSection" to "กลุ่ม", + "diary.leader" to "ผู้ดูแล", + "diary.min" to "นาที", + "diary.minutes" to "นาที", + "diary.mustCreateAtLeastTwoGroups" to "การสร้างครั้งแรกต้องมีอย่างน้อย 2 กลุ่ม", + "diary.nextAppointment" to "นัดถัดไป", + "diary.noActiveTrainingDay" to "ไม่ได้เลือกวันฝึกซ้อม", + "diary.noEntries" to "ไม่มีรายการ", + "diary.noFreeActivitiesYet" to "ยังไม่มีการบันทึกกิจกรรมอิสระ", + "diary.noParticipants" to "ไม่มีผู้เข้าร่วมสำหรับวันฝึกซ้อมนี้", + "diary.numberOfGroups" to "จำนวนกลุ่ม", + "diary.oneGroupAdded" to "เพิ่มกลุ่มสำเร็จ 1 กลุ่ม", + "diary.openPlanItems" to "เปิดอยู่ {count} รายการ", + "diary.openPlanItemsLabel" to "สถานะแผน", + "diary.overallActivity" to "กิจกรรมรวม", + "diary.participants" to "ผู้เข้าร่วม", + "diary.participantStatusCancelled" to "ยกเลิก", + "diary.participantStatusExcused" to "ลา", + "diary.participantStatusNone" to "ไม่มีสถานะ", + "diary.planActivitiesCount" to "กิจกรรมในแผน", + "diary.planAddHint" to "คุณสามารถเพิ่มรายการใหม่ได้จากปุ่มด้านบน", + "diary.planEmptyState" to "ยังไม่มีรายการในแผนการฝึกซ้อม", + "diary.quickAdd" to "+ เพิ่มด่วน", + "diary.searchParticipants" to "ค้นหาผู้เข้าร่วม", + "diary.selectGroup" to "เลือกกลุ่ม...", + "diary.selectGroupAndActivity" to "กรุณาเลือกกลุ่มและกรอกกิจกรรม", + "diary.selectParticipantAndNote" to "กรุณาเลือกผู้เข้าร่วมและใส่ข้อความบันทึก", + "diary.selectTags" to "เลือกแท็ก", + "diary.selectTrainingGroup" to "เลือกกลุ่มฝึกซ้อม", + "diary.selectTrainingGroupPlaceholder" to "กรุณาเลือก...", + "diary.showImage" to "แสดงรูป/ภาพวาด", + "diary.skipSuggestion" to "ดำเนินการต่อโดยไม่ใช้ข้อเสนอแนะ", + "diary.standardActivities" to "กิจกรรมมาตรฐาน", + "diary.standardActivityAddError" to "ไม่สามารถเพิ่มกิจกรรมมาตรฐานได้", + "diary.standardDurationShort" to "นาที", + "diary.startTime" to "เวลาเริ่ม", + "diary.statusEmpty" to "วันฝึกซ้อมนี้ยังว่างอยู่", + "diary.statusInProgress" to "วันฝึกซ้อมนี้เตรียมไว้เพียงบางส่วน", + "diary.statusOpenShort" to "เปิด", + "diary.statusReady" to "ตั้งค่าเวลาและแผนการฝึกซ้อมแล้ว", + "diary.statusReadyShort" to "พร้อม", + "diary.suggestion" to "ข้อเสนอแนะ", + "diary.timeblock" to "ช่วงเวลา", + "diary.timeblocksCount" to "ช่วงเวลา", + "diary.title" to "สมุดบันทึกการฝึกซ้อม", + "diary.today" to "วันนี้", + "diary.trainingDayAsPDF" to "ดาวน์โหลดวันฝึกซ้อมเป็น PDF", + "diary.trainingDayAsPDFShort" to "วันฝึกซ้อม PDF", + "diary.trainingDayChecklist" to "รายการตรวจสอบความครบถ้วน", + "diary.trainingDaySection" to "วันฝึกซ้อม", + "diary.trainingDaySummaryPdfShort" to "สรุปผู้เข้าร่วม PDF", + "diary.trainingEnd" to "สิ้นสุดการฝึกซ้อม", + "diary.trainingPlan" to "แผนการฝึกซ้อม", + "diary.trainingPlanAsPDF" to "แผนการฝึกซ้อมเป็น PDF", + "diary.trainingPlanPdfShort" to "แผนการดำเนินงาน PDF", + "diary.trainingStart" to "เริ่มฝึกซ้อม", + "diary.trainingTimesUpdated" to "อัปเดตเวลาฝึกซ้อมสำเร็จ", + "diary.trainingWindow" to "ช่วงเวลาฝึกซ้อม", + "diary.trainingWindowUnset" to "ยังไม่ได้ตั้งค่า", + "diary.unassignedPlanItems" to "ยังไม่กำหนด {count} รายการ", + "diary.updateTimes" to "อัปเดตเวลา", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (เยอรมัน)", + "languages.de-CH" to "Schwiizerdütsch (เยอรมันสวิส)", + "languages.en-AU" to "English (อังกฤษ (AU))", + "languages.en-GB" to "English (อังกฤษ (GB))", + "languages.en-US" to "English (อังกฤษ (US))", + "languages.es" to "Español (สเปน)", + "languages.fil" to "Filipino (ฟิลิปปินส์)", + "languages.fr" to "Français (ฝรั่งเศส)", + "languages.it" to "Italiano (อิตาลี)", + "languages.ja" to "日本語 (ญี่ปุ่น)", + "languages.pl" to "Polski (โปแลนด์)", + "languages.th" to "ไทย", + "languages.tl" to "Tagalog (ตากาล็อก)", + "languages.zh" to "中文 (จีน)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "การดำเนินการ", + "members.active" to "ใช้งานอยู่", + "members.activeMembers" to "สมาชิกที่ใช้งานอยู่", + "members.addEmail" to "เพิ่มอีเมล", + "members.addGroup" to "เพิ่มกลุ่ม...", + "members.addPhone" to "เพิ่มหมายเลขโทรศัพท์", + "members.address" to "ที่อยู่", + "members.adultReleaseApproved" to "อนุมัติสำหรับผู้ใหญ่", + "members.adultReserveApproved" to "ตัวสำรองผู้ใหญ่", + "members.adults" to "ผู้ใหญ่ (20+)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "จาก", + "members.ageGroup" to "กลุ่มอายุ", + "members.ageRange" to "ช่วงอายุ", + "members.ageToPlaceholder" to "ถึง", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "วันเกิด", + "members.bulkActions" to "การดำเนินการแบบกลุ่ม", + "members.camera" to "กล้อง", + "members.change" to "เปลี่ยน", + "members.city" to "เมือง", + "members.clearFields" to "ล้างช่องข้อมูล", + "members.clearFilters" to "รีเซ็ตตัวกรอง", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "สร้าง", + "members.createNewMember" to "สร้างสมาชิกใหม่", + "members.dataIssueAddress" to "ไม่มีที่อยู่", + "members.dataIssueBirthdate" to "ไม่มีวันเกิด", + "members.dataIssueCity" to "ไม่มีเมือง", + "members.dataIssueEmail" to "ไม่มีอีเมล", + "members.dataIssueGender" to "เพศยังไม่ชัดเจน", + "members.dataIssuePhone" to "ไม่มีหมายเลขโทรศัพท์", + "members.dataIssuePostalCode" to "ไม่มีรหัสไปรษณีย์", + "members.dataIssueStreet" to "ไม่มีถนน", + "members.dataIssueTrainingGroup" to "ไม่มีกลุ่มฝึกซ้อม", + "members.dataQuality" to "คุณภาพข้อมูล", + "members.dataQualityComplete" to "ข้อมูลครบถ้วน", + "members.deactivateMember" to "ปิดการใช้งานสมาชิก", + "members.deactivateMemberConfirm" to "คุณต้องการปิดการใช้งาน \"{name}\" จริงหรือไม่?", + "members.deactivateMemberTitle" to "ปิดการใช้งานสมาชิก", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "แก้ไขสมาชิก", + "members.editorAssignTrainingGroupHint" to "กรุณากำหนดกลุ่มฝึกซ้อมอย่างน้อยหนึ่งกลุ่ม", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "ข้อมูลที่แนะนำให้กรอก", + "members.emailAddress" to "อีเมล", + "members.emailAddressShort" to "อีเมล", + "members.emails" to "อีเมล", + "members.errorAddingToGroup" to "เกิดข้อผิดพลาดในการเพิ่มเข้ากลุ่ม", + "members.errorDeactivatingMember" to "เกิดข้อผิดพลาดในการปิดการใช้งานสมาชิก", + "members.errorDeletingImage" to "ไม่สามารถลบรูปภาพได้", + "members.errorLoadingImage" to "เกิดข้อผิดพลาดในการโหลดรูปภาพ", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "เกิดข้อผิดพลาดในการโหลดการเข้าร่วมฝึกซ้อม", + "members.errorMarkingForm" to "เกิดข้อผิดพลาดในการทำเครื่องหมายแบบฟอร์ม", + "members.errorRemovingFromGroup" to "เกิดข้อผิดพลาดในการลบออกจากกลุ่ม", + "members.errorRemovingTestMembership" to "เกิดข้อผิดพลาดในการลบสถานะสมาชิกทดลอง", + "members.errorRotatingImage" to "เกิดข้อผิดพลาดในการหมุนรูปภาพ", + "members.errorSavingMember" to "เกิดข้อผิดพลาดในการบันทึกสมาชิก", + "members.errorSettingPrimaryImage" to "ไม่สามารถตั้งรูปภาพหลักได้", + "members.errorTransfer" to "เกิดข้อผิดพลาดในการโอนข้อมูล", + "members.errorUpdatingRatings" to "เกิดข้อผิดพลาดในการอัปเดตค่า TTR/QTTR", + "members.errorUploadingImage" to "ไม่สามารถอัปโหลดรูปภาพได้", + "members.exercises" to "แบบฝึก", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "ชื่อ", + "members.formHandedOver" to "ส่งแบบฟอร์มสมาชิกแล้ว", + "members.formMarkedAsHandedOver" to "ทำเครื่องหมายว่าได้ส่งแบบฟอร์มสมาชิกแล้ว", + "members.gender" to "เพศ", + "members.genderDiverse" to "หลากหลาย", + "members.genderFemale" to "หญิง", + "members.genderMale" to "ชาย", + "members.genderUnknown" to "ไม่ทราบ", + "members.generatePhoneList" to "สร้างรายการโทรศัพท์", + "members.groupPhotoCrop" to "ประมวลผลรูปกลุ่ม", + "members.groupPhotoCropSaved" to "บันทึกรูปสมาชิกจากรูปกลุ่มแล้ว", + "members.image" to "รูปภาพ", + "members.imageDeleted" to "ลบรูปภาพแล้ว", + "members.imageInternet" to "รูปภาพ (เน็ต)", + "members.imagePreview" to "ตัวอย่างรูปสมาชิก", + "members.imageUpdated" to "อัปเดตรูปภาพแล้ว", + "members.inactive" to "ไม่ใช้งาน", + "members.inactiveMembers" to "สมาชิกที่ไม่ใช้งาน", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17 (17 ปีหรือน้อยกว่า)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "นามสกุล", + "members.lastTrainingFilter" to "การเข้าร่วมล่าสุด", + "members.lastTrainingFilterHasDate" to "มีวันที่บันทึก", + "members.lastTrainingFilterHint" to "ตาราง: ฤดูกาลปัจจุบันในคอลัมน์ AK รายละเอียดครบ (สองฤดูกาล, การเข้าร่วมล่าสุด, จำนวนครั้ง) เมื่อชี้ที่แถว", + "members.lastTrainingFilterNoDate" to "ไม่มีการเข้าร่วมล่าสุด", + "members.lastTrainingFilterNotInTraining" to "ทำเครื่องหมาย «ไม่เข้าร่วมอีกต่อไป»", + "members.loadingMembers" to "Loading members...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "ปิดการใช้งานสมาชิกแล้ว", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "ส่งแบบฟอร์มสมาชิกแล้ว", + "members.memberImage" to "รูปสมาชิก", + "members.memberImages" to "รูปสมาชิก", + "members.memberInfo" to "ข้อมูลสมาชิก", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "ไม่มีการบันทึก myTischtennis ID", + "members.name" to "ชื่อ นามสกุล", + "members.newMember" to "สมาชิกใหม่", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "ยังไม่ได้กำหนดกลุ่ม", + "members.noGroupsAvailable" to "ไม่มีกลุ่มให้เลือก", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "บันทึกย่อ", + "members.noTestMembership" to "ไม่ใช่สมาชิกทดลองอีกต่อไป", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "จะแสดงเฉพาะสมาชิกที่ใช้งานอยู่", + "members.openTasks" to "Open tasks", + "members.parent" to "ผู้ปกครอง", + "members.parentFallback" to "Parent", + "members.parentName" to "ชื่อ (เช่น แม่ พ่อ)", + "members.phoneList" to "รายการโทรศัพท์.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "หมายเลขโทรศัพท์", + "members.phoneNumberShort" to "โทรศัพท์", + "members.phones" to "หมายเลขโทรศัพท์", + "members.picsInInternetAllowed" to "อนุญาตให้ใช้รูปบนอินเทอร์เน็ต", + "members.postalCode" to "รหัสไปรษณีย์", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "หลัก", + "members.primaryImageUpdated" to "อัปเดตรูปภาพหลักแล้ว", + "members.remove" to "เอาออก", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "ใช้งานอยู่ + ข้อมูลไม่ครบ", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "ข้อมูลไม่ครบ", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "ค้นหาและตัวกรอง", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "เลือกไฟล์", + "members.showInactiveMembers" to "แสดงสมาชิกที่ไม่ใช้งาน", + "members.showTrainingParticipationsColumn" to "แสดงคอลัมน์ « การเข้าร่วมเทรนนิง »", + "members.showTtrHistory" to "แสดงประวัติ TTR", + "members.sixOrMoreParticipations" to "เข้าร่วมฝึกซ้อม 6 ครั้งขึ้นไป", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "ค่า QTTR", + "members.status" to "สถานะ", + "members.street" to "ถนน", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "เปิด", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "กำหนดกลุ่มฝึกซ้อม", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "ตรวจสอบคุณภาพข้อมูล", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "ทดลอง", + "members.testMembers" to "สมาชิกทดลอง", + "members.testMembership" to "สมาชิกทดลอง", + "members.testMembershipRemoved" to "ลบสถานะสมาชิกทดลองแล้ว", + "members.threeOrMoreParticipations" to "เข้าร่วมฝึกซ้อม 3 ครั้งขึ้นไป", + "members.title" to "สมาชิก", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "กลุ่มฝึกซ้อม", + "members.trainingParticipations" to "การเข้าร่วมฝึกซ้อม", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "โอนสมาชิก", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "ผู้ใหญ่ (ไม่ใช่เยาวชนตามวันตัด)", + "members.ttAgeClassCol" to "รุ่นอายุ (TT)", + "members.ttFilterGroupJ" to "ชายและหญิง (รวม)", + "members.ttFilterGroupM" to "เฉพาะหญิง", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "ปัจจุบัน", + "members.ttSeasonFilter" to "ฤดูกาล (วันตัดสิทธิ์)", + "members.ttSeasonNextTag" to "ถัดไป", + "members.ttStichtagHint" to "วันตัดสิทธิ์ 1 ม.ค. (DTTB) ชาย: เฉพาะ J หญิง: J และ M", + "members.updateRatings" to "อัปเดต TTR/QTTR จาก myTischtennis", + "members.updating" to "กำลังอัปเดต...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "ต้องการเล่น", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "ยกเลิก", + "messages.confirm" to "ยืนยัน", + "messages.error" to "ข้อผิดพลาด", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "ข้อมูล", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "สำเร็จ", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "คำเตือน", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "การอนุมัติ", + "navigation.backToHome" to "กลับหน้าแรก", + "navigation.billing" to "การเรียกเก็บเงิน", + "navigation.clickTtAccount" to "บัญชี HTTV / click-TT", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "การตั้งค่าสโมสร", + "navigation.clubTournaments" to "การแข่งขันของสโมสร", + "navigation.competitions" to "การแข่งขัน", + "navigation.dailyBusiness" to "ธุรกิจประจำวัน", + "navigation.diary" to "ไดอารี่", + "navigation.home" to "หน้าแรก", + "navigation.login" to "เข้าสู่ระบบ", + "navigation.logout" to "ออกจากระบบ", + "navigation.logs" to "บันทึกระบบ", + "navigation.members" to "สมาชิก", + "navigation.memberTransfer" to "การโอนสมาชิก", + "navigation.myTischtennisAccount" to "บัญชี myTischtennis", + "navigation.orders" to "คำสั่งซื้อ", + "navigation.permissions" to "สิทธิ์", + "navigation.personalSettings" to "การตั้งค่าส่วนตัว", + "navigation.predefinedActivities" to "กิจกรรมที่กำหนดไว้ล่วงหน้า", + "navigation.register" to "ลงทะเบียน", + "navigation.schedule" to "ตารางเวลา", + "navigation.settings" to "การตั้งค่า", + "navigation.statistics" to "สถิติการฝึกซ้อม", + "navigation.teamManagement" to "การจัดการทีม", + "navigation.tournamentParticipations" to "การเข้าร่วมการแข่งขัน", + "navigation.tournaments" to "การแข่งขัน", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "สร้างคำสั่งซื้อ", + "orders.budget" to "Budget", + "orders.club" to "สโมสร", + "orders.cost" to "ค่าใช้จ่าย", + "orders.dateAutoHint" to "ระบบจะกำหนดวันที่ให้อัตโนมัติ และทุกการเปลี่ยนแปลงจะถูกบันทึกพร้อมวันที่", + "orders.errorLoading" to "ไม่สามารถโหลดคำสั่งซื้อได้", + "orders.errorSaving" to "ไม่สามารถบันทึกคำสั่งซื้อได้", + "orders.filterAllClubs" to "ทุกสโมสร", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "ทุกสถานะ", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "สามารถดูและจัดการคำสั่งซื้อทั้งหมดข้ามทุกสโมสรได้ที่นี่", + "orders.globalTitle" to "คำสั่งซื้อของทุกสโมสร", + "orders.history" to "ประวัติ", + "orders.item" to "สินค้า", + "orders.itemPlaceholder" to "เช่น เสื้อแข่ง ฮู้ดดี้ หรือซองไม้", + "orders.loading" to "กำลังโหลดคำสั่งซื้อ...", + "orders.member" to "สมาชิก", + "orders.memberTitle" to "คำสั่งซื้อ: {name}", + "orders.noOrdersGlobal" to "ขณะนี้ยังไม่มีคำสั่งซื้อ", + "orders.noOrdersMember" to "ยังไม่มีคำสั่งซื้อสำหรับสมาชิกคนนี้", + "orders.open" to "ค้างชำระ", + "orders.orderDate" to "วันที่สร้าง", + "orders.paid" to "ชำระแล้ว", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "ค้นหาตามสโมสร สมาชิก หรือสินค้า", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "สถานะ", + "orders.statusArrived" to "สินค้ามาถึงแล้ว", + "orders.statusDate" to "การเปลี่ยนแปลงล่าสุด", + "orders.statusHandedOver" to "ส่งมอบสินค้าแล้ว", + "orders.statusOrdered" to "สั่งแล้ว", + "orders.statusRequested" to "ต้องการ", + "orders.title" to "คำสั่งซื้อ", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "สโมสร:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "สร้างเมื่อ:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "ลีก:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "ลงทะเบียนสำหรับ:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "ลีกที่วางแผน:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "ฤดูกาล:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "กลุ่มอายุของทีม:", + "pdfGenerator.teamGenderLabel" to "เพศทีม:", + "pdfGenerator.teamLineupTitle" to "รายชื่อทีม", + "pdfGenerator.teamNameLabel" to "ทีม:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "ภาษา", + "settings.languageChanged" to "เปลี่ยนภาษาสำเร็จ", + "settings.languageDescription" to "เลือกภาษาที่คุณต้องการสำหรับแอปพลิเคชัน", + "settings.personalSettings" to "การตั้งค่าส่วนตัว", + "settings.selectLanguage" to "เลือกภาษา", + "settings.title" to "การตั้งค่า", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "ผู้เล่นที่พร้อมใช้งาน", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "สิทธิ์ลงเล่น", + "teamManagement.eligibilityAdultRelease" to "อนุมัติให้เล่นผู้ใหญ่", + "teamManagement.eligibilityAdultReleaseAndReserve" to "อนุมัติ + ตัวสำรองผู้ใหญ่", + "teamManagement.eligibilityAdultReserve" to "ตัวสำรองผู้ใหญ่", + "teamManagement.eligibilityRegular" to "ปกติ", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "ส่งออกรายชื่อเป็น PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "ครึ่งแรก", + "teamManagement.firstHalfFull" to "ครึ่งแรก (กรกฎาคม - ธันวาคม)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "โปรดเลือกระดับลีกและบันทึก", + "teamManagement.lineupEmpty" to "ยังไม่มีการกำหนดผู้เล่นให้ทีมนี้", + "teamManagement.lineupPdfEmpty" to "ไม่มีผู้เล่นในรายชื่อ – ไม่สามารถสร้าง PDF ได้", + "teamManagement.lineupPdfFilePrefix" to "รายชื่อทีม", + "teamManagement.lineupProposal" to "จัดทีมตาม QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "ไม่สามารถบันทึกรายชื่อทีมได้", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} มีคะแนน QTTR มากกว่า {lower} เกิน 30 คะแนน กรุณาแก้ไขลำดับนี้", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "รายการที่ขาด:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "วางและแยกวิเคราะห์ URL ของ MyTischtennis เพื่อเชื่อมโยง ID ทีมและข้อมูลลีก", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "ลีกที่วางแผน", + "teamManagement.plannedLeagueHint" to "ไม่บังคับ แยกจากลีกที่ลงทะเบียนแล้ว (MyTischtennis)", + "teamManagement.plannedLeaguePlaceholder" to "เช่น ลีกในเขต หลังการลงทะเบียนครั้งถัดไป …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "ครึ่งหลัง", + "teamManagement.secondHalfFull" to "ครึ่งหลัง (ตั้งแต่ 1 มกราคม)", + "teamManagement.selectedLineup" to "ผู้เล่นที่ขึ้นทะเบียน", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "เพิ่มกฎกลุ่ม", + "tournaments.address" to "ที่อยู่", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "ข้อมูลผู้เข้าร่วมทั้งหมดครบถ้วน", + "tournaments.allDataCompleteTop3" to "ข้อมูล 3 อันดับแรกครบถ้วน", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "กรุณาเพิ่มกฎกลุ่มอย่างน้อยหนึ่งกฎสำหรับ {label} (เช่น อันดับ 1,2)", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "แก้ไข", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "สร้างรอบชิงจากรอบกลาง", + "tournaments.createFinalFromPreliminary" to "สร้างรอบชิงจากรอบคัดเลือก", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "สร้างรอบกลางจากรอบคัดเลือก", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "ยังไม่ได้บันทึก", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "คุณต้องการลบคลาส \"{name}\" จริงหรือไม่?", + "tournaments.deleteClassParticipantsDetached" to "ผู้เข้าร่วมทั้งหมดจะถูกเอาออกจากคลาสนี้", + "tournaments.deleteClassTitle" to "ลบคลาส", + "tournaments.deleteExistingPairingsConfirm" to "คู่ที่มีอยู่จะถูกลบ ดำเนินการต่อหรือไม่?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "จัดสรรโต๊ะว่าง", + "tournaments.distributeTablesResult" to "การจัดสรรโต๊ะ", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "เหมาะกับ: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "กรุณากรอกชื่อคลาส", + "tournaments.enterExternalParticipantName" to "กรุณากรอกอย่างน้อยชื่อและนามสกุล", + "tournaments.enterMiniLocation" to "กรุณากรอกสถานที่", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "เกิดข้อผิดพลาดในการจัดสรรโต๊ะ", + "tournaments.errorGeneratingPdf" to "เกิดข้อผิดพลาดในการสร้าง PDF", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "รอบชิง", + "tournaments.finishMatch" to "จบการแข่งขัน", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "กำลังสร้าง PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "กลุ่ม", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "รอบกลาง (รอบ 2)", + "tournaments.internalStatsAbsoluteRank" to "อันดับผลรวม", + "tournaments.internalStatsAgeFilter" to "รุ่นอายุและเพศ (เดี่ยว)", + "tournaments.internalStatsAgeFilterAll" to "ทุกรุ่นอายุ", + "tournaments.internalStatsAgeFilterNone" to "ไม่ได้เลือกรุ่นอายุ", + "tournaments.internalStatsAgeNoClass" to "ไม่มีคลาส", + "tournaments.internalStatsAgeSelectAll" to "ทั้งหมด", + "tournaments.internalStatsAgeSelectNone" to "ไม่มี", + "tournaments.internalStatsAverageRank" to "อันดับเฉลี่ย (ต่อการแข่งขัน)", + "tournaments.internalStatsAvgPoints" to "เฉลี่ย", + "tournaments.internalStatsEmpty" to "ไม่มีข้อมูลในช่วงเวลาที่เลือก", + "tournaments.internalStatsExportPdf" to "ส่งออก PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "12 เดือนล่าสุด", + "tournaments.internalStatsLast3Months" to "3 เดือนล่าสุด", + "tournaments.internalStatsLast6Months" to "6 เดือนล่าสุด", + "tournaments.internalStatsOpenButton" to "เปิดสถิติ (เดี่ยว)", + "tournaments.internalStatsPeriod" to "ช่วงเวลา", + "tournaments.internalStatsPoints" to "รวม", + "tournaments.internalStatsPointsExplain" to "การให้คะแนน: ในแต่ละกลุ่มใช้เปอร์เซ็นต์ตามอันดับ (มี N คนที่มีอันดับ: ที่ 1 = 100% คนสุดท้าย = 0% ระหว่างกลางเชิงเส้น; อันดับเดียวกันค่าเดียวกัน) N นับทุกคนที่มีอันดับในกลุ่ม (รวมแขก) มีผู้เล่นเพียงคนเดียว = 100% รอบ KO: ค่ากลุ่มสูงสุดของคลาส +1 แล้ว +1 ต่อชนะแมตช์ KO เฉพาะสมาชิก เดี่ยว", + "tournaments.internalStatsTitle" to "สถิติการแข่งขันภายใน (เดี่ยว)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} การแข่งขันในช่วงเวลา (ไม่รวมมินิแชมเปียนชิป)", + "tournaments.internalStatsTtAdult" to "ประเภทใหญ่", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "ทำเครื่องหมายว่ากำลังแข่ง", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "ต้องมีผู้เข้าร่วมอย่างน้อย 2 คนสำหรับการจับคู่", + "tournaments.missingDataPDF" to "ข้อมูลที่ขาดหายเป็น PDF", + "tournaments.missingDataPDFSubtitle" to "กรุณาเก็บข้อมูลที่ขาดหาย (ทำเครื่องหมาย ____) จากผู้เข้าร่วมและจดบันทึกที่นี่", + "tournaments.missingDataPDFSubtitleTop3" to "กรุณาเก็บข้อมูลที่ขาดหายของ 3 อันดับแรก (ทำเครื่องหมาย ____) และจดบันทึกที่นี่", + "tournaments.missingDataPDFTitle" to "ข้อมูลผู้เข้าร่วมที่ขาดหาย – มินิแชมเปี้ยนชิพ", + "tournaments.missingDataPDFTitleTop3" to "ข้อมูลที่ขาดหาย – 3 อันดับแรก มินิแชมเปี้ยนชิพ", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "เซตใหม่ เช่น 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "ไม่มีแมตช์ที่ผู้เล่นทั้งสองว่าง", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "ไม่พบคลาสที่เหมาะสม", + "tournaments.noFreeTables" to "ไม่มีโต๊ะว่าง", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "ไม่พบแมตช์กำพร้า", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "ยังไม่มีกฎ ตัวอย่าง: อันดับ 1 และ 2 -> กลุ่มบนของรอบ 2", + "tournaments.noPoolRulesYetFinal" to "ยังไม่มีกฎ ตัวอย่าง: อันดับ 1 และ 2 -> รอบชิง", + "tournaments.noTop3Yet" to "ยังไม่ได้กำหนด 3 อันดับแรก", + "tournaments.noTournamentDate" to "ไม่มีวันที่ของทัวร์นาเมนต์", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "ไม่พบวันฝึกสำหรับ {date}", + "tournaments.noTrainingParticipants" to "ไม่พบผู้เข้าร่วมการฝึกสำหรับวันที่นี้", + "tournaments.noValidTrainingParticipants" to "ไม่พบผู้เข้าร่วมการฝึกที่ถูกต้องสำหรับวันที่นี้", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "จำนวนโต๊ะ", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "ลบแมตช์กำพร้า {count} รายการแล้ว", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "หน้า", + "tournaments.pairingAlreadyExists" to "คู่นี้มีอยู่แล้ว", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "สร้างคู่แล้ว {successCount} คู่, มีข้อผิดพลาด {errorCount} รายการ", + "tournaments.participantConflicts" to "ความขัดแย้ง", + "tournaments.participantNotFound" to "ไม่พบผู้เข้าร่วม", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "โทรศัพท์", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "อันดับจากแต่ละกลุ่ม (เช่น 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "ผู้เล่น 1", + "tournaments.playerTwo" to "ผู้เล่น 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "แข่งชิงอันดับสาม", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "ผ่านเข้ารอบ: รอบคัดเลือก → รอบกลาง (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "ผ่านเข้ารอบ: รอบ {from} → รอบ {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "อันดับ", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "จำนวนกลุ่ม", + "tournaments.roundMode" to "โหมด", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "บันทึกรอบ", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "กรุณาเลือกทัวร์นาเมนต์ก่อน", + "tournaments.selectTwoDifferentPlayers" to "กรุณาเลือกผู้เล่นสองคนที่แตกต่างกัน", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "การตอบกลับจากเซิร์ฟเวอร์ไม่ถูกต้อง", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "รอบกลางเป็นตัวเลือก หากเปิดใช้งาน จะมีรอบชิงตามมาเสมอ รอบชิงแบบน็อกเอาต์จะถูกสร้างเป็นสายเดียว", + "tournaments.stageConfigLoadError" to "เกิดข้อผิดพลาดในการโหลดการตั้งค่ารอบ", + "tournaments.stageConfigLoading" to "กำลังโหลดการตั้งค่ารอบ…", + "tournaments.stageConfigMissingIds" to "ไม่สามารถบันทึกได้: ไม่มีรหัสสโมสรหรือรหัสทัวร์นาเมนต์", + "tournaments.stageConfigTitle" to "รอบกลางและรอบชิง", + "tournaments.stageCreated" to "สร้างรอบ {round} แล้ว", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "เสร็จแล้ว", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "กำลังแข่ง", + "tournaments.statusOpen" to "เปิด", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "โต๊ะ", + "tournaments.tablesDistributed" to "จัดสรรโต๊ะเรียบร้อยแล้ว", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "เป้าหมาย", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "จำนวนกลุ่มปลายทาง", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "หญิง", + "tournaments.tournamentClassGenderOpen" to "เปิด (ทุกคน)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "ลบเครื่องหมายกำลังแข่ง", + "tournaments.unpairedDoubles" to "ประเภทคู่ที่ยังไม่มีคู่", + "tournaments.useIntermediateStage" to "ใช้รอบกลาง", + "tournaments.warningBirthDateMissing" to "คลาสนี้ต้องมีวันเกิด", + "tournaments.warningClassMissing" to "ไม่พบคลาส", + "tournaments.warningGenderMismatch" to "เพศไม่ตรงกับคลาส", + "tournaments.warningGenderMissing" to "คลาสนี้ต้องมีข้อมูลเพศ", + "tournaments.warningMissingPairing" to "ยังไม่มีคู่สำหรับประเภทคู่", + "tournaments.warningTooOldForClass" to "อายุมากเกินไปสำหรับคลาสนี้", + "tournaments.warningTooYoungForClass" to "อายุน้อยเกินไปสำหรับคลาสนี้", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "การดำเนินการ", + "trainingStats.activeMembers" to "สมาชิกที่ใช้งานอยู่", + "trainingStats.allTrainingDays" to "วันฝึกซ้อมทั้งหมด", + "trainingStats.attendingMembers" to "สมาชิกที่เข้าร่วม", + "trainingStats.averageParticipationCurrentMonth" to "การเข้าร่วมเฉลี่ย (เดือนปัจจุบัน)", + "trainingStats.averageParticipationHalfYear" to "การเข้าร่วมเฉลี่ย (ครึ่งปี)", + "trainingStats.averageParticipationLastMonth" to "การเข้าร่วมเฉลี่ย (เดือนที่ผ่านมา)", + "trainingStats.averageParticipationQuarter" to "การเข้าร่วมเฉลี่ย (ไตรมาส)", + "trainingStats.averageParticipationYear" to "การเข้าร่วมเฉลี่ย (ปี)", + "trainingStats.birthdate" to "วันเกิด", + "trainingStats.date" to "วันที่", + "trainingStats.lastTraining" to "การฝึกซ้อมล่าสุด", + "trainingStats.memberParticipations" to "การเข้าร่วมของสมาชิก", + "trainingStats.name" to "ชื่อ", + "trainingStats.noParticipants" to "ไม่มีผู้เข้าร่วม", + "trainingStats.participants" to "ผู้เข้าร่วม", + "trainingStats.participations12Months" to "การเข้าร่วม (12 เดือน)", + "trainingStats.participations3Months" to "การเข้าร่วม (3 เดือน)", + "trainingStats.participationsTotal" to "การเข้าร่วม (ทั้งหมด)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "แสดงรายละเอียด", + "trainingStats.title" to "สถิติการฝึกซ้อม", + "trainingStats.trainingDayFilter" to "วันฝึกซ้อม", + "trainingStats.trainingDays" to "วันฝึกซ้อม (12 เดือนล่าสุด)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "วันในสัปดาห์", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val tl: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "Training Diary", + "app.title" to "Training Diary", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "Nabigo ang activation. Suriin ang link.", + "auth.confirmPassword" to "Kumpirmahin ang password", + "auth.email" to "Email", + "auth.forgotPassword" to "Nakalimutan ang password?", + "auth.forgotPasswordDescription" to "Ilagay ang iyong email address. Makakatanggap ka ng link para i-reset ang password.", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "Mag-login", + "auth.loginFailed" to "Nabigo ang pag-login. Suriin ang iyong credentials.", + "auth.loginSuccess" to "Matagumpay na nag-login", + "auth.logout" to "Mag-logout", + "auth.logoutSuccess" to "Matagumpay na nag-logout", + "auth.newPassword" to "Bagong password", + "auth.noAccount" to "Wala pang account?", + "auth.password" to "Password", + "auth.passwordResetSuccess" to "Matagumpay na nabago ang password. Maaari ka nang mag-login.", + "auth.passwordsDoNotMatch" to "Hindi magkatugma ang mga password.", + "auth.passwordTooShort" to "Ang password ay dapat hindi bababa sa 6 na character.", + "auth.register" to "Magrehistro", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "Tandaan ako", + "auth.resetEmailSent" to "Kung may account sa email na ito, ipinadala na ang reset link. Suriin ang inbox (at spam folder).", + "auth.resetFailed" to "Hindi nabago ang password. Maaaring nag-expire na ang link.", + "auth.resetPassword" to "Magtakda ng bagong password", + "auth.resetRequestFailed" to "Nabigo ang request. Subukan ulit.", + "auth.saveNewPassword" to "I-save ang password", + "auth.saving" to "Sine-save...", + "auth.sending" to "Ipinapadala...", + "auth.sendResetLink" to "Ipadala ang link", + "auth.sessionExpired" to "Nag-expire na ang iyong session. Ikaw ay ma-logout.", + "auth.toLogin" to "Pumunta sa login", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "Hindi pinapayagan ang access sa club na ito.", + "club.accessRequested" to "Naipadala na ang hiling sa access.", + "club.accessRequestFailed" to "Hindi naipadala ang hiling sa access.", + "club.accessRequestPending" to "Humiling ka na ng access sa club na ito. Maghintay lamang.", + "club.create" to "Gumawa ng club", + "club.createTitle" to "Gumawa ng club", + "club.diary" to "Talaarawan ng pagsasanay", + "club.errorLoadingRequests" to "Error sa pag-load ng mga nakabinbing hiling", + "club.load" to "I-load", + "club.members" to "Mga miyembro", + "club.mobileSelectHint" to "Pumili muna ng club para magamit ang app sa smartphone.", + "club.name" to "Pangalan ng club", + "club.new" to "Bagong club", + "club.noAccess" to "Wala ka pang pahintulot na ma-access ang club na ito.", + "club.openAccessRequests" to "Mga nakabinbing hiling sa access", + "club.openRequests" to "Mga nakabinbing hiling sa access", + "club.requestAccess" to "Humiling ng access", + "club.select" to "Pumili ng club", + "club.selectPlaceholder" to "Pumili ng club...", + "club.title" to "Club", + "club.trainingDiary" to "Talaarawan ng pagsasanay", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "Mga aksyon", + "common.active" to "Aktibo", + "common.add" to "Magdagdag", + "common.all" to "Lahat", + "common.apply" to "Ilapat", + "common.back" to "Bumalik", + "common.cancel" to "Kanselahin", + "common.choose" to "Pumili", + "common.clear" to "Burahin", + "common.close" to "Isara", + "common.confirm" to "Kumpirmahin", + "common.create" to "Lumikha", + "common.date" to "Petsa", + "common.days" to "Araw", + "common.delete" to "Tanggalin", + "common.description" to "Paglalarawan", + "common.details" to "Mga detalye", + "common.disabled" to "Hindi pinagana", + "common.download" to "I-download", + "common.edit" to "I-edit", + "common.enabled" to "Pinagana", + "common.filter" to "I-filter", + "common.hours" to "Oras", + "common.in" to "sa", + "common.inactive" to "Hindi aktibo", + "common.loading" to "Naglo-load...", + "common.min" to "Min", + "common.minutes" to "Minuto", + "common.months" to "Buwan", + "common.move" to "Ilipat", + "common.name" to "Pangalan", + "common.new" to "Bago", + "common.next" to "Susunod", + "common.no" to "Hindi", + "common.ok" to "OK", + "common.optional" to "Opsyonal", + "common.period" to "Panahon", + "common.previous" to "Nakaraan", + "common.refresh" to "I-reload", + "common.remove" to "Alisin", + "common.required" to "Kinakailangan", + "common.reset" to "I-reset", + "common.save" to "I-save", + "common.saved" to "Na-save", + "common.saving" to "Sine-save...", + "common.search" to "Maghanap", + "common.select" to "Pumili", + "common.status" to "Katayuan", + "common.submit" to "Ipasa", + "common.time" to "Oras", + "common.today" to "Ngayon", + "common.type" to "Uri", + "common.update" to "I-update", + "common.view" to "Tingnan", + "common.weeks" to "Linggo", + "common.years" to "Taon", + "common.yes" to "Oo", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "Magdagdag ng palo", + "courtDrawingTool.backhand" to "Backhand", + "courtDrawingTool.codeLabel" to "Code", + "courtDrawingTool.completeFirstStroke" to "Tapusin ang unang palo", + "courtDrawingTool.completeFirstStrokeHint" to "Piliin ang panimulang posisyon, side, spin at target. Pagkatapos ay lalabas ang graphic.", + "courtDrawingTool.configureExercise" to "I-configure ang ehersisyo", + "courtDrawingTool.counterSpin" to "Counter spin", + "courtDrawingTool.forehand" to "Forehand", + "courtDrawingTool.noAdditionalStrokes" to "Wala pang mga kasunod na palo.", + "courtDrawingTool.preview" to "Preview", + "courtDrawingTool.previewHint" to "Lilitaw ang graphic kapag kumpleto na ang unang palo.", + "courtDrawingTool.service" to "Serve:", + "courtDrawingTool.serviceTitle" to "Serve", + "courtDrawingTool.sidespin" to "Sidespin", + "courtDrawingTool.sideUnderspin" to "Side underspin", + "courtDrawingTool.spin" to "Spin:", + "courtDrawingTool.startLeft" to "kaliwa", + "courtDrawingTool.startMiddle" to "gitna", + "courtDrawingTool.startRight" to "kanan", + "courtDrawingTool.stepAdditionalStrokes" to "4. Mga kasunod na palo", + "courtDrawingTool.stepAdditionalStrokesHint" to "Opsyonal na magdagdag pa ng mga bola bilang listahan.", + "courtDrawingTool.stepFirstStroke" to "2. Unang palo", + "courtDrawingTool.stepFirstStrokeHint" to "Itakda ang side at spin para sa unang bola.", + "courtDrawingTool.stepStartPosition" to "1. Panimulang posisyon", + "courtDrawingTool.stepStartPositionHint" to "Saang posisyon ng serve magsisimula ang ehersisyo?", + "courtDrawingTool.stepTarget" to "3. Target", + "courtDrawingTool.stepTargetHint" to "Pumili ng target zone para sa unang palo.", + "courtDrawingTool.strokeSide" to "Side", + "courtDrawingTool.strokeTypeBlock" to "Block", + "courtDrawingTool.strokeTypeChopDefense" to "Chop defense", + "courtDrawingTool.strokeTypeCounter" to "Counter", + "courtDrawingTool.strokeTypeFlip" to "Flip", + "courtDrawingTool.strokeTypeLabel" to "Uri ng palo", + "courtDrawingTool.strokeTypeLobDefense" to "Lob defense", + "courtDrawingTool.strokeTypePush" to "Push", + "courtDrawingTool.strokeTypeSmash" to "Smash", + "courtDrawingTool.strokeTypeTopspin" to "Topspin", + "courtDrawingTool.targetBackhandHalfLong" to "backhand half-long", + "courtDrawingTool.targetBackhandLong" to "backhand long", + "courtDrawingTool.targetBackhandShort" to "backhand short", + "courtDrawingTool.targetForehandHalfLong" to "forehand half-long", + "courtDrawingTool.targetForehandLong" to "forehand long", + "courtDrawingTool.targetForehandShort" to "forehand short", + "courtDrawingTool.targetMiddleHalfLong" to "middle half-long", + "courtDrawingTool.targetMiddleLong" to "middle long", + "courtDrawingTool.targetMiddleShort" to "middle short", + "courtDrawingTool.targetPosition" to "Posisyon ng target:", + "courtDrawingTool.targetPositionLabel" to "Posisyon ng target", + "courtDrawingTool.title" to "Guhit ng ehersisyo sa table tennis", + "courtDrawingTool.titleLabel" to "Pamagat", + "courtDrawingTool.topspin" to "Topspin", + "courtDrawingTool.toTarget" to "papunta sa", + "courtDrawingTool.underspin" to "Underspin", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "Aktibong araw ng pagsasanay", + "diary.activities" to "Mga aktibidad", + "diary.activity" to "Aktibidad", + "diary.activityDrawing" to "Guhit ng aktibidad", + "diary.activityImage" to "Larawan ng aktibidad", + "diary.activityNotFound" to "Hindi nahanap ang aktibidad. Mangyaring pumili mula sa listahan.", + "diary.activityOrTimeblock" to "Aktibidad / time block", + "diary.activityPlaceholder" to "Aktibidad", + "diary.activityRequired" to "Mangyaring maglagay ng aktibidad.", + "diary.addActivity" to "Magdagdag ng aktibidad", + "diary.addGroup" to "Magdagdag ng grupo", + "diary.addGroupActivity" to "Magdagdag ng aktibidad ng grupo", + "diary.addGroupButton" to "+ Grupo", + "diary.addTimeblock" to "Time block", + "diary.all" to "Lahat", + "diary.applySuggestion" to "Ilapat ang mungkahi", + "diary.assignParticipants" to "Magtalaga ng mga kalahok", + "diary.assignParticipantsForGroupActivity" to "Magtalaga ng mga kalahok para sa aktibidad ng grupo", + "diary.assignShort" to "Italaga", + "diary.bookAccident" to "Magtala ng aksidente", + "diary.confirmDelete" to "Kumpirmahin ang pagbura", + "diary.confirmDeleteDate" to "Sigurado ka bang buburahin ang petsang ito?", + "diary.confirmDeleteDateDetails" to "Mabubura rin ang lahat ng kaugnay na datos.", + "diary.confirmDeleteGroup" to "Sigurado ka bang buburahin ang grupong \"{name}\"?", + "diary.createDate" to "Lumikha ng petsa", + "diary.createDrawing" to "Gumawa ng guhit ng ehersisyo", + "diary.createGroups" to "Lumikha ng mga grupo", + "diary.createNew" to "Lumikha ng bago", + "diary.createNewDate" to "Lumikha ng bagong petsa", + "diary.date" to "Petsa", + "diary.dateCannotBeDeleted" to "Hindi mabubura ang petsa", + "diary.dateCannotBeDeletedDetails" to "May laman pa (plano ng pagsasanay, mga kalahok, aktibidad, aksidente o mga tala).", + "diary.dateNoLongerCurrent" to "Hindi na kasalukuyan ang napiling petsa. Pakisubukan muli.", + "diary.delete" to "Burahin", + "diary.deleteDate" to "Burahin ang petsa", + "diary.deleteGroup" to "Burahin ang grupo", + "diary.duration" to "Tagal", + "diary.durationExampleLong" to "hal. 2x7 o 3*5", + "diary.durationExampleShort" to "hal. 2x7", + "diary.durationMinutes" to "Tagal (min)", + "diary.editActivity" to "I-edit ang aktibidad", + "diary.editGroupActivity" to "I-edit ang aktibidad ng grupo", + "diary.editTrainingTimes" to "I-edit ang oras ng pagsasanay", + "diary.errorCreatingActivity" to "Error sa paggawa ng aktibidad", + "diary.errorCreatingGroups" to "Error sa paggawa ng mga grupo", + "diary.errorDeletingGroup" to "Error sa pagbura ng grupo", + "diary.errorLoadingPredefinedActivities" to "Error sa pag-load ng paunang natukoy na mga aktibidad", + "diary.errorMarkingForm" to "Error sa pagmamarka ng form ng miyembro", + "diary.errorOccurred" to "May naganap na error. Pakisubukang muli.", + "diary.existingGroups" to "Mga kasalukuyang grupo", + "diary.filterAbsent" to "Liban", + "diary.filterAll" to "Lahat", + "diary.filterExcused" to "May paalam", + "diary.filterPresent" to "Dumalo", + "diary.filterTest" to "Trial", + "diary.formHandedOver" to "Naibigay na ang form ng miyembro", + "diary.formMarkedAsHandedOver" to "Namarkahan bilang naibigay na ang form ng miyembro", + "diary.freeActivities" to "Libreng aktibidad", + "diary.gallery" to "Gallery ng mga miyembro", + "diary.galleryCreating" to "Ginagawa ang gallery…", + "diary.group" to "Grupo...", + "diary.groupDeletedSuccessfully" to "Matagumpay na nabura ang grupo!", + "diary.groupManagement" to "Pamamahala ng grupo", + "diary.groupsCreated" to "Matagumpay na nalikha ang {count} grupo!", + "diary.groupsLabel" to "Mga grupo", + "diary.groupsSection" to "Mga grupo", + "diary.leader" to "Tagapanguna", + "diary.min" to "Min", + "diary.minutes" to "Minuto", + "diary.mustCreateAtLeastTwoGroups" to "Sa unang paggawa, kailangan ng hindi bababa sa 2 grupo!", + "diary.nextAppointment" to "Susunod na schedule", + "diary.noActiveTrainingDay" to "Walang napiling araw ng pagsasanay.", + "diary.noEntries" to "Walang entry", + "diary.noFreeActivitiesYet" to "Wala pang naitalang libreng aktibidad.", + "diary.noParticipants" to "Walang kalahok para sa araw ng pagsasanay na ito.", + "diary.numberOfGroups" to "Bilang ng grupo", + "diary.oneGroupAdded" to "Matagumpay na naidagdag ang 1 grupo!", + "diary.openPlanItems" to "{count} bukas", + "diary.openPlanItemsLabel" to "Katayuan ng plano", + "diary.overallActivity" to "Kabuuang aktibidad", + "diary.participants" to "Mga kalahok", + "diary.participantStatusCancelled" to "Kinansela", + "diary.participantStatusExcused" to "May paalam", + "diary.participantStatusNone" to "Walang katayuan", + "diary.planActivitiesCount" to "Mga aktibidad sa plano", + "diary.planAddHint" to "Magdagdag ng bagong item sa plano gamit ang mga aksyon sa itaas.", + "diary.planEmptyState" to "Wala pang nakalagay sa plano ng pagsasanay.", + "diary.quickAdd" to "+ Quick add", + "diary.searchParticipants" to "Maghanap ng kalahok", + "diary.selectGroup" to "Pumili ng grupo...", + "diary.selectGroupAndActivity" to "Mangyaring pumili ng grupo at maglagay ng aktibidad.", + "diary.selectParticipantAndNote" to "Mangyaring pumili ng kalahok at maglagay ng tala.", + "diary.selectTags" to "Pumili ng mga tag", + "diary.selectTrainingGroup" to "Pumili ng grupo ng pagsasanay", + "diary.selectTrainingGroupPlaceholder" to "Mangyaring pumili...", + "diary.showImage" to "Ipakita ang larawan/guhit", + "diary.skipSuggestion" to "Magpatuloy nang walang mungkahi", + "diary.standardActivities" to "Mga karaniwang aktibidad", + "diary.standardActivityAddError" to "Hindi maidagdag ang karaniwang aktibidad.", + "diary.standardDurationShort" to "min", + "diary.startTime" to "Oras ng simula", + "diary.statusEmpty" to "Wala pang laman ang araw ng pagsasanay na ito.", + "diary.statusInProgress" to "Bahagyang naihanda ang araw ng pagsasanay na ito.", + "diary.statusOpenShort" to "Bukas", + "diary.statusReady" to "Naayos na ang oras at plano ng pagsasanay.", + "diary.statusReadyShort" to "Handa", + "diary.suggestion" to "Mungkahi", + "diary.timeblock" to "Time block", + "diary.timeblocksCount" to "Mga time block", + "diary.title" to "Talaarawan ng pagsasanay", + "diary.today" to "Ngayon", + "diary.trainingDayAsPDF" to "I-download ang araw ng pagsasanay bilang PDF", + "diary.trainingDayAsPDFShort" to "Araw ng pagsasanay PDF", + "diary.trainingDayChecklist" to "Checklist ng pagkumpleto", + "diary.trainingDaySection" to "Araw ng pagsasanay", + "diary.trainingDaySummaryPdfShort" to "Buod ng kalahok bilang PDF", + "diary.trainingEnd" to "Pagtatapos ng pagsasanay", + "diary.trainingPlan" to "Plano ng pagsasanay", + "diary.trainingPlanAsPDF" to "Plano ng pagsasanay bilang PDF", + "diary.trainingPlanPdfShort" to "Flow plan bilang PDF", + "diary.trainingStart" to "Simula ng pagsasanay", + "diary.trainingTimesUpdated" to "Matagumpay na na-update ang oras ng pagsasanay.", + "diary.trainingWindow" to "Oras ng pagsasanay", + "diary.trainingWindowUnset" to "Hindi pa naitakda", + "diary.unassignedPlanItems" to "{count} hindi nakatalaga", + "diary.updateTimes" to "I-update ang oras", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (Aleman)", + "languages.de-CH" to "Schwiizerdütsch (Swiss Aleman)", + "languages.en-AU" to "English (Ingles (AU))", + "languages.en-GB" to "English (Ingles (GB))", + "languages.en-US" to "English (Ingles (US))", + "languages.es" to "Español (Espanyol)", + "languages.fil" to "Filipino", + "languages.fr" to "Français (Pranses)", + "languages.it" to "Italiano (Italyano)", + "languages.ja" to "日本語 (Hapones)", + "languages.pl" to "Polski (Polako)", + "languages.th" to "ไทย (Thai)", + "languages.tl" to "Tagalog", + "languages.zh" to "中文 (Intsik)", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "Mga aksyon", + "members.active" to "Aktibo", + "members.activeMembers" to "Mga aktibong miyembro", + "members.addEmail" to "Magdagdag ng email address", + "members.addGroup" to "Magdagdag ng grupo...", + "members.addPhone" to "Magdagdag ng numero ng telepono", + "members.address" to "Address", + "members.adultReleaseApproved" to "Aprubado para sa matatanda", + "members.adultReserveApproved" to "Adult reserve", + "members.adults" to "Adulto (20+)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "mula", + "members.ageGroup" to "Pangkat ng edad", + "members.ageRange" to "Edad mula - hanggang", + "members.ageToPlaceholder" to "hanggang", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "Petsa ng kapanganakan", + "members.bulkActions" to "Mga bulk action", + "members.camera" to "Camera", + "members.change" to "Baguhin", + "members.city" to "Lungsod", + "members.clearFields" to "Burahin ang mga field", + "members.clearFilters" to "I-reset ang mga filter", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "Lumikha", + "members.createNewMember" to "Lumikha ng bagong miyembro", + "members.dataIssueAddress" to "Kulang ang address", + "members.dataIssueBirthdate" to "Kulang ang petsa ng kapanganakan", + "members.dataIssueCity" to "Kulang ang lungsod", + "members.dataIssueEmail" to "Kulang ang email", + "members.dataIssueGender" to "Hindi malinaw ang kasarian", + "members.dataIssuePhone" to "Kulang ang telepono", + "members.dataIssuePostalCode" to "Kulang ang postal code", + "members.dataIssueStreet" to "Kulang ang kalye", + "members.dataIssueTrainingGroup" to "Kulang ang grupo ng pagsasanay", + "members.dataQuality" to "Kalidad ng datos", + "members.dataQualityComplete" to "Kumpleto ang datos", + "members.deactivateMember" to "I-deactivate ang miyembro", + "members.deactivateMemberConfirm" to "Sigurado ka bang ide-deactivate si \"{name}\"?", + "members.deactivateMemberTitle" to "I-deactivate ang miyembro", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "I-edit ang miyembro", + "members.editorAssignTrainingGroupHint" to "Mangyaring magtalaga ng kahit isang grupo ng pagsasanay.", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "Inirerekomendang entry", + "members.emailAddress" to "Email address", + "members.emailAddressShort" to "Email", + "members.emails" to "Mga email address", + "members.errorAddingToGroup" to "Error sa pagdagdag sa grupo", + "members.errorDeactivatingMember" to "Error sa pag-deactivate ng miyembro", + "members.errorDeletingImage" to "Hindi mabura ang larawan", + "members.errorLoadingImage" to "Error sa pag-load ng larawan", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "Error sa pag-load ng paglahok sa pagsasanay", + "members.errorMarkingForm" to "Error sa pagmamarka ng form.", + "members.errorRemovingFromGroup" to "Error sa pag-alis mula sa grupo", + "members.errorRemovingTestMembership" to "Error sa pag-alis ng trial membership.", + "members.errorRotatingImage" to "Error sa pag-ikot ng larawan", + "members.errorSavingMember" to "Error sa pag-save ng miyembro", + "members.errorSettingPrimaryImage" to "Hindi maitakda ang pangunahing larawan", + "members.errorTransfer" to "Error sa paglipat", + "members.errorUpdatingRatings" to "Error sa pag-update ng mga TTR/QTTR value", + "members.errorUploadingImage" to "Hindi ma-upload ang larawan", + "members.exercises" to "Mga ehersisyo", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "Pangalan", + "members.formHandedOver" to "Naibigay na ang form ng miyembro", + "members.formMarkedAsHandedOver" to "Namarkahan bilang naibigay na ang form ng miyembro.", + "members.gender" to "Kasarian", + "members.genderDiverse" to "Iba pa", + "members.genderFemale" to "Babae", + "members.genderMale" to "Lalaki", + "members.genderUnknown" to "Hindi alam", + "members.generatePhoneList" to "Gumawa ng listahan ng telepono", + "members.groupPhotoCrop" to "Iproseso ang group photo", + "members.groupPhotoCropSaved" to "Na-save ang larawan ng miyembro mula sa group photo.", + "members.image" to "Larawan", + "members.imageDeleted" to "Nabura na ang larawan.", + "members.imageInternet" to "Larawan (net?)", + "members.imagePreview" to "Preview ng larawan ng miyembro", + "members.imageUpdated" to "Na-update na ang larawan", + "members.inactive" to "hindi aktibo", + "members.inactiveMembers" to "Mga hindi aktibong miyembro", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17 (17 pababa)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "Apelyido", + "members.lastTrainingFilter" to "Huling training", + "members.lastTrainingFilterHasDate" to "May petsa", + "members.lastTrainingFilterHint" to "Table: kasalukuyang season sa AK. Buong detalye (dalawang season, huling training, participations) kapag-hover sa hilera.", + "members.lastTrainingFilterNoDate" to "Walang huling training", + "members.lastTrainingFilterNotInTraining" to "Flag na “hindi na nagte-training”", + "members.loadingMembers" to "Loading members...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "Na-deactivate na ang miyembro", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "Naibigay na ang form ng miyembro", + "members.memberImage" to "Larawan ng miyembro", + "members.memberImages" to "Mga larawan ng miyembro", + "members.memberInfo" to "Impormasyon ng miyembro", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "Walang naka-save na myTischtennis ID", + "members.name" to "Apelyido, Pangalan", + "members.newMember" to "Bagong miyembro", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "Walang grupong nakatalaga", + "members.noGroupsAvailable" to "Walang available na grupo", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "Mga tala", + "members.noTestMembership" to "Hindi na trial member", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "Tanging mga aktibong miyembro lamang ang ilalabas", + "members.openTasks" to "Open tasks", + "members.parent" to "Magulang", + "members.parentFallback" to "Parent", + "members.parentName" to "Pangalan (hal. ina, ama)", + "members.phoneList" to "ListahanNgTelepono.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "Numero ng telepono", + "members.phoneNumberShort" to "Telepono", + "members.phones" to "Mga numero ng telepono", + "members.picsInInternetAllowed" to "Pinapayagan ang larawan sa internet", + "members.postalCode" to "Postal code", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "Pangunahin", + "members.primaryImageUpdated" to "Na-update na ang pangunahing larawan.", + "members.remove" to "Alisin", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "Aktibo + hindi kumpleto ang datos", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "Hindi kumpleto ang datos", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "Paghahanap at mga filter", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "Pumili ng file", + "members.showInactiveMembers" to "Ipakita ang mga hindi aktibong miyembro", + "members.showTrainingParticipationsColumn" to "Ipakita ang column na « Training participations »", + "members.showTtrHistory" to "Ipakita ang TTR history", + "members.sixOrMoreParticipations" to "6 o higit pang paglahok sa pagsasanay", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "Halaga ng QTTR", + "members.status" to "Katayuan", + "members.street" to "Kalye", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "Buksan", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "Magtalaga ng grupo ng pagsasanay", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "Suriin ang kalidad ng datos", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "Trial", + "members.testMembers" to "Mga trial na miyembro", + "members.testMembership" to "Trial membership", + "members.testMembershipRemoved" to "Inalis na ang trial membership.", + "members.threeOrMoreParticipations" to "3 o higit pang paglahok sa pagsasanay", + "members.title" to "Mga miyembro", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "Mga grupo ng pagsasanay", + "members.trainingParticipations" to "Paglahok sa pagsasanay", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "Ilipat ang mga miyembro", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "Adulto (hindi youth sa cutoff)", + "members.ttAgeClassCol" to "Edad (TT)", + "members.ttFilterGroupJ" to "Babae at lalaki (halo)", + "members.ttFilterGroupM" to "Babae lamang", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "kasalukuyan", + "members.ttSeasonFilter" to "Season (cutoff)", + "members.ttSeasonNextTag" to "susunod", + "members.ttStichtagHint" to "Cutoff Enero 1 (DTTB). Lalaki: J lang. Babae: J at M.", + "members.updateRatings" to "I-update ang TTR/QTTR mula sa myTischtennis", + "members.updating" to "Nag-a-update...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "Gustong maglaro", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "Kanselahin", + "messages.confirm" to "Kumpirmahin", + "messages.error" to "Error", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "Impormasyon", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "Tagumpay", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "Babala", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "Mga pag-apruba", + "navigation.backToHome" to "Bumalik sa home", + "navigation.billing" to "Pagsingil", + "navigation.clickTtAccount" to "HTTV / click-TT Account", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "Mga setting ng club", + "navigation.clubTournaments" to "Mga paligsahan ng club", + "navigation.competitions" to "Mga kompetisyon", + "navigation.dailyBusiness" to "Araw-araw na negosyo", + "navigation.diary" to "Talaarawan", + "navigation.home" to "Home", + "navigation.login" to "Mag-login", + "navigation.logout" to "Mag-logout", + "navigation.logs" to "Mga system log", + "navigation.members" to "Mga miyembro", + "navigation.memberTransfer" to "Paglipat ng miyembro", + "navigation.myTischtennisAccount" to "myTischtennis Account", + "navigation.orders" to "Mga order", + "navigation.permissions" to "Mga pahintulot", + "navigation.personalSettings" to "Mga personal na setting", + "navigation.predefinedActivities" to "Mga paunang natukoy na aktibidad", + "navigation.register" to "Magrehistro", + "navigation.schedule" to "Mga iskedyul", + "navigation.settings" to "Mga setting", + "navigation.statistics" to "Mga istatistika ng pagsasanay", + "navigation.teamManagement" to "Pamamahala ng koponan", + "navigation.tournamentParticipations" to "Mga paglahok sa paligsahan", + "navigation.tournaments" to "Mga paligsahan", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "Gumawa ng order", + "orders.budget" to "Budget", + "orders.club" to "Club", + "orders.cost" to "Halaga", + "orders.dateAutoHint" to "Awtomatikong itinatakda ang petsa at bawat pagbabago ay sine-save kasama ang petsa.", + "orders.errorLoading" to "Hindi ma-load ang mga order.", + "orders.errorSaving" to "Hindi ma-save ang order.", + "orders.filterAllClubs" to "Lahat ng club", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "Lahat ng status", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "Dito makikita at mapapamahalaan ang lahat ng order mula sa iba’t ibang club.", + "orders.globalTitle" to "Mga order ng lahat ng club", + "orders.history" to "Kasaysayan", + "orders.item" to "Item", + "orders.itemPlaceholder" to "hal. jersey, hoodie, o lagayan ng raketa", + "orders.loading" to "Nilo-load ang mga order...", + "orders.member" to "Miyembro", + "orders.memberTitle" to "Mga order: {name}", + "orders.noOrdersGlobal" to "Wala pang mga order sa ngayon.", + "orders.noOrdersMember" to "Wala pang order para sa miyembrong ito.", + "orders.open" to "Natitira", + "orders.orderDate" to "Ginawa noong", + "orders.paid" to "Nabayaran", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "Maghanap ayon sa club, miyembro, o item", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "Status", + "orders.statusArrived" to "dumating na ang item", + "orders.statusDate" to "Huling pagbabago", + "orders.statusHandedOver" to "naibigay na ang item", + "orders.statusOrdered" to "na-order na", + "orders.statusRequested" to "hinihiling", + "orders.title" to "Mga order", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "Club:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "Nilikha:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "Liga:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "Registration para sa:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "Plano na liga:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "Season:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "Age group ng koponan:", + "pdfGenerator.teamGenderLabel" to "Kasarian ng koponan:", + "pdfGenerator.teamLineupTitle" to "Line-up ng koponan", + "pdfGenerator.teamNameLabel" to "Koponan:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "Wika", + "settings.languageChanged" to "Matagumpay na nagbago ang wika", + "settings.languageDescription" to "Pumili ng iyong gustong wika para sa aplikasyon", + "settings.personalSettings" to "Mga personal na setting", + "settings.selectLanguage" to "Pumili ng wika", + "settings.title" to "Mga setting", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "Magagamit na manlalaro", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "Pagiging karapat-dapat", + "teamManagement.eligibilityAdultRelease" to "Pinayagan sa adults", + "teamManagement.eligibilityAdultReleaseAndReserve" to "Pinayagan + reserve sa adults", + "teamManagement.eligibilityAdultReserve" to "Reserve sa adults", + "teamManagement.eligibilityRegular" to "Karaniwan", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "I-export ang line-up bilang PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "Unang yugto", + "teamManagement.firstHalfFull" to "Unang yugto (Hulyo - Disyembre)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "Mangyaring pumili ng liga at mag-save.", + "teamManagement.lineupEmpty" to "Wala pang manlalarong itinalaga sa koponang ito.", + "teamManagement.lineupPdfEmpty" to "Walang manlalaro sa line-up – hindi makagawa ng PDF.", + "teamManagement.lineupPdfFilePrefix" to "Line-up", + "teamManagement.lineupProposal" to "Line-up ayon sa QTTR", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "Hindi mai-save ang line-up ng koponan.", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "Mahigit 30 QTTR points ang lamang ni {higher} kay {lower}. Pakitama ang ayos na ito.", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "Nawawalang impormasyon:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "I-paste at i-parse ang URL ng MyTischtennis para i-link ang team ID at datos ng liga.", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "Plano na liga", + "teamManagement.plannedLeagueHint" to "Opsyonal. Hiwalay sa nakarehistrong liga (MyTischtennis).", + "teamManagement.plannedLeaguePlaceholder" to "hal. district league, pagkatapos ng susunod na rehistro …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "Ikalawang yugto", + "teamManagement.secondHalfFull" to "Ikalawang yugto (mula Enero 1)", + "teamManagement.selectedLineup" to "Napiling manlalaro", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "Magdagdag ng pool rule", + "tournaments.address" to "Address", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "Kumpleto na ang lahat ng datos ng mga kalahok.", + "tournaments.allDataCompleteTop3" to "Kumpleto na ang lahat ng datos ng unang 3 puwesto.", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "Mangyaring magdagdag ng kahit isang pool rule para sa {label} (hal. places 1,2).", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "Itama", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "Gumawa ng final round mula sa intermediate round", + "tournaments.createFinalFromPreliminary" to "Gumawa ng final round mula sa preliminary round", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "Gumawa ng intermediate round mula sa preliminary round", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "Hindi pa naitala", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "Sigurado ka bang buburahin ang class na \"{name}\"?", + "tournaments.deleteClassParticipantsDetached" to "Aalisin ang lahat ng participants sa class na ito.", + "tournaments.deleteClassTitle" to "Burahin ang class", + "tournaments.deleteExistingPairingsConfirm" to "Buburahin ang mga kasalukuyang pairing. Magpatuloy?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "Ipamahagi ang mga bakanteng mesa", + "tournaments.distributeTablesResult" to "Pamamahagi ng mesa", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "Suitable for: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "Mangyaring maglagay ng pangalan ng class.", + "tournaments.enterExternalParticipantName" to "Mangyaring ilagay kahit first name at last name.", + "tournaments.enterMiniLocation" to "Mangyaring maglagay ng lokasyon.", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "May error sa pamamahagi ng mga mesa.", + "tournaments.errorGeneratingPdf" to "Error sa pagbuo ng PDF.", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "Final round", + "tournaments.finishMatch" to "Tapusin", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "Gumagawa ng PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "Mga grupo", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "Intermediate round (round 2)", + "tournaments.internalStatsAbsoluteRank" to "Ranggo sa kabuuang marka", + "tournaments.internalStatsAgeFilter" to "Edad at kasarian (singles)", + "tournaments.internalStatsAgeFilterAll" to "Lahat ng pangkat ng edad", + "tournaments.internalStatsAgeFilterNone" to "Walang napiling pangkat ng edad", + "tournaments.internalStatsAgeNoClass" to "Walang klase", + "tournaments.internalStatsAgeSelectAll" to "Lahat", + "tournaments.internalStatsAgeSelectNone" to "Wala", + "tournaments.internalStatsAverageRank" to "Ranggo sa average (bawat tournament)", + "tournaments.internalStatsAvgPoints" to "Avg.", + "tournaments.internalStatsEmpty" to "Walang datos sa napiling panahon.", + "tournaments.internalStatsExportPdf" to "I-export bilang PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "Huling 12 buwan", + "tournaments.internalStatsLast3Months" to "Huling 3 buwan", + "tournaments.internalStatsLast6Months" to "Huling 6 na buwan", + "tournaments.internalStatsOpenButton" to "Buksan ang istatistika (singles)", + "tournaments.internalStatsPeriod" to "Saklaw", + "tournaments.internalStatsPoints" to "Kabuuan", + "tournaments.internalStatsPointsExplain" to "Marka: sa bawat grupo ang puwesto ay nasa porsyento (may N na may ranggo: 1 = 100%, huli = 0%, linear sa gitna; parehong ranggo = parehong halaga). Kinakabilang ang lahat ng may ranggo sa grupo (kasama ang bisita). Isang manlalaro lamang: 100%. Sa KO: pinakamataas na grupo ng klase + 1, +1 bawat panalong laro sa KO. Mga miyembro lamang (singles).", + "tournaments.internalStatsTitle" to "Istatistika ng internal na tournament (singles)", + "tournaments.internalStatsTournamentsInPeriod" to "{count} tournament sa panahon (hindi kasama ang mini championships).", + "tournaments.internalStatsTtAdult" to "Adults", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "Markahan bilang live", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "Kailangan ng hindi bababa sa 2 participants para sa pairings.", + "tournaments.missingDataPDF" to "Nawawalang datos bilang PDF", + "tournaments.missingDataPDFSubtitle" to "Pakikolekta ang nawawalang datos (may markang ____) mula sa mga kalahok at itala dito.", + "tournaments.missingDataPDFSubtitleTop3" to "Pakikolekta ang nawawalang datos ng unang 3 (may markang ____) at itala dito.", + "tournaments.missingDataPDFTitle" to "Nawawalang datos ng mga kalahok – Mini championship", + "tournaments.missingDataPDFTitleTop3" to "Nawawalang datos – Top 3 Mini championship", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "Bagong set, hal. 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "Walang laban kung saan pareho ang mga manlalaro ay bakante.", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "No suitable class found.", + "tournaments.noFreeTables" to "Walang bakanteng mesa.", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "Walang nahanap na orphaned matches.", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "Wala pang rules. Halimbawa: places 1 at 2 -> top round-2 groups.", + "tournaments.noPoolRulesYetFinal" to "Wala pang rules. Halimbawa: places 1 at 2 -> final round.", + "tournaments.noTop3Yet" to "Hindi pa natutukoy ang unang 3 puwesto.", + "tournaments.noTournamentDate" to "Walang tournament date.", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "Walang training session para sa {date}.", + "tournaments.noTrainingParticipants" to "Walang participants na nakita sa training session para sa petsang ito.", + "tournaments.noValidTrainingParticipants" to "Walang valid na participants na nakita sa training session para sa petsang ito.", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "Bilang ng mesa", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "{count} orphaned matches ang naalis.", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "Pahina", + "tournaments.pairingAlreadyExists" to "May ganitong pairing na.", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "{successCount} pairings ang nagawa, {errorCount} errors.", + "tournaments.participantConflicts" to "Conflicts", + "tournaments.participantNotFound" to "Hindi nakita ang participant.", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "Telepono", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "Mga puwesto mula sa bawat grupo (hal. 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "Manlalaro 1", + "tournaments.playerTwo" to "Manlalaro 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "Maglaro para sa ikatlong puwesto", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "Advance: preliminary round → intermediate round (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "Advance: round {from} → round {to}", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "Ranggo", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "Bilang ng mga grupo", + "tournaments.roundMode" to "Mode", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "I-save ang rounds", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "Mangyaring pumili muna ng tournament.", + "tournaments.selectTwoDifferentPlayers" to "Mangyaring pumili ng dalawang magkaibang player.", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "Hindi wastong tugon mula sa server.", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "Opsyonal ang intermediate round. Kapag in-activate mo ito, laging may susunod na final round. Ang knockout final round ay gagawin bilang isang bracket.", + "tournaments.stageConfigLoadError" to "Error sa pag-load ng round configuration.", + "tournaments.stageConfigLoading" to "Naglo-load ng round configuration…", + "tournaments.stageConfigMissingIds" to "Hindi ma-save: kulang ang club o tournament ID.", + "tournaments.stageConfigTitle" to "Intermediate at Final Round", + "tournaments.stageCreated" to "Nagawa ang round {round}.", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "Tapos", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "Live", + "tournaments.statusOpen" to "Bukas", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "Mesa", + "tournaments.tablesDistributed" to "Ang mga mesa ay naipamahagi na.", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "Target", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "Target na bilang ng grupo", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "Babae", + "tournaments.tournamentClassGenderOpen" to "Bukas (lahat)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "Alisin ang live marker", + "tournaments.unpairedDoubles" to "Doubles without partner", + "tournaments.useIntermediateStage" to "Gamitin ang intermediate round", + "tournaments.warningBirthDateMissing" to "Birth date is missing for this class", + "tournaments.warningClassMissing" to "Class not found", + "tournaments.warningGenderMismatch" to "Gender does not match class", + "tournaments.warningGenderMissing" to "Gender is missing for this class", + "tournaments.warningMissingPairing" to "Doubles partner missing", + "tournaments.warningTooOldForClass" to "Too old for this class", + "tournaments.warningTooYoungForClass" to "Too young for this class", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "Mga aksyon", + "trainingStats.activeMembers" to "Mga aktibong miyembro", + "trainingStats.allTrainingDays" to "Lahat ng araw ng pagsasanay", + "trainingStats.attendingMembers" to "Mga dumalong miyembro", + "trainingStats.averageParticipationCurrentMonth" to "Karaniwang paglahok (kasalukuyang buwan)", + "trainingStats.averageParticipationHalfYear" to "Karaniwang paglahok (kalahating taon)", + "trainingStats.averageParticipationLastMonth" to "Karaniwang paglahok (nakaraang buwan)", + "trainingStats.averageParticipationQuarter" to "Karaniwang paglahok (quarter)", + "trainingStats.averageParticipationYear" to "Karaniwang paglahok (taon)", + "trainingStats.birthdate" to "Petsa ng kapanganakan", + "trainingStats.date" to "Petsa", + "trainingStats.lastTraining" to "Huling pagsasanay", + "trainingStats.memberParticipations" to "Paglahok ng miyembro", + "trainingStats.name" to "Pangalan", + "trainingStats.noParticipants" to "Walang kalahok", + "trainingStats.participants" to "Mga kalahok", + "trainingStats.participations12Months" to "Paglahok (12 buwan)", + "trainingStats.participations3Months" to "Paglahok (3 buwan)", + "trainingStats.participationsTotal" to "Paglahok (kabuuan)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "Ipakita ang mga detalye", + "trainingStats.title" to "Mga istatistika ng pagsasanay", + "trainingStats.trainingDayFilter" to "Araw ng pagsasanay", + "trainingStats.trainingDays" to "Mga araw ng pagsasanay (huling 12 buwan)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "Araw ng linggo", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val zh: Map by lazy { mapOf( + "accident.accident" to "Unfall", + "accident.accidentDescription" to "Beschreibung des Unfalls...", + "accident.close" to "Schließen", + "accident.member" to "Mitglied", + "accident.pleaseSelect" to "Bitte wählen", + "accident.reportAccident" to "Unfall melden", + "accident.reportedAccidents" to "Gemeldete Unfälle", + "accident.submit" to "Eintragen", + "activityStats.activityStatistics" to "Statistik der Übungen", + "activityStats.last3Participations" to "Letzte 3 Teilnahmen", + "activityStats.noParticipations" to "Keine Teilnahmen vorhanden", + "activityStats.noStatistics" to "Keine Statistiken vorhanden", + "activityStats.title" to "Übungs-Statistiken", + "app.name" to "训练日记", + "app.title" to "训练日记", + "auth.accountActivated" to "Account aktiviert! Du kannst dich jetzt anmelden.", + "auth.activate" to "Aktivieren", + "auth.activateAccount" to "Account aktivieren", + "auth.activationFailed" to "激活失败。请检查链接。", + "auth.confirmPassword" to "确认密码", + "auth.email" to "电子邮件", + "auth.forgotPassword" to "忘记密码?", + "auth.forgotPasswordDescription" to "输入您的电子邮件地址。您将收到重置密码的链接。", + "auth.hasAccount" to "Bereits ein Konto?", + "auth.login" to "登录", + "auth.loginFailed" to "登录失败。请检查您的凭据。", + "auth.loginSuccess" to "登录成功", + "auth.logout" to "退出登录", + "auth.logoutSuccess" to "退出成功", + "auth.newPassword" to "新密码", + "auth.noAccount" to "还没有账号?", + "auth.password" to "密码", + "auth.passwordResetSuccess" to "密码已成功更改。您现在可以使用新密码登录。", + "auth.passwordsDoNotMatch" to "密码不匹配。", + "auth.passwordTooShort" to "密码至少需要6个字符。", + "auth.register" to "注册", + "auth.registerFailed" to "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "auth.registerSuccess" to "Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mails, um den Account zu aktivieren.", + "auth.rememberMe" to "记住我", + "auth.resetEmailSent" to "如果该邮箱存在账号,已发送重置链接。请检查收件箱(和垃圾邮件文件夹)。", + "auth.resetFailed" to "无法更改密码。链接可能已过期。", + "auth.resetPassword" to "设置新密码", + "auth.resetRequestFailed" to "请求失败。请重试。", + "auth.saveNewPassword" to "保存密码", + "auth.saving" to "保存中...", + "auth.sending" to "发送中...", + "auth.sendResetLink" to "发送链接", + "auth.sessionExpired" to "您的会话已过期。您将被登出。", + "auth.toLogin" to "去登录", + "baseDialog.close" to "Schließen", + "baseDialog.minimize" to "Minimieren", + "baseDialog.resize" to "Größe ändern", + "billing.autoSuggestMapping" to "Auto-Vorschläge", + "billing.deleteBilling" to "Löschen", + "billing.deleteConfirm" to "Diese erzeugte Abrechnung wirklich löschen?", + "billing.deleteError" to "Abrechnung konnte nicht gelöscht werden.", + "billing.deleteSuccess" to "Abrechnung wurde gelöscht.", + "billing.deleteTemplate" to "Vorlage löschen", + "billing.deleteTemplateConfirm" to "Diese Vorlage wirklich löschen?", + "billing.deleteTemplateError" to "Vorlage konnte nicht gelöscht werden.", + "billing.deleteTemplateSuccess" to "Vorlage wurde gelöscht.", + "billing.documentDate" to "Datum", + "billing.downloadError" to "PDF konnte nicht heruntergeladen werden.", + "billing.downloadPdf" to "PDF herunterladen", + "billing.generateAndDownloadPdf" to "PDF erzeugen + herunterladen", + "billing.generateError" to "PDF konnte nicht erzeugt werden.", + "billing.generateOwnBilling" to "Eigene Abrechnung erzeugen", + "billing.generatePdf" to "PDF erzeugen", + "billing.generateSuccess" to "PDF wurde erzeugt.", + "billing.generatingPdf" to "Erzeuge PDF...", + "billing.hourlyRate" to "Stunden-Gehalt", + "billing.hoursAutoHint" to "Anzahl Stunden wird automatisch aus den Trainingstagen berechnet: {hours} h ({count} Einheiten).", + "billing.hoursTotal" to "Anzahl Stunden", + "billing.iban" to "IBAN", + "billing.ibanBoxesReset" to "IBAN-Boxen wurden zurückgesetzt.", + "billing.ibanLearnCancel" to "IBAN-Lernen abbrechen", + "billing.ibanLearnDone" to "IBAN-Boxen wurden aus dem gewählten Bereich zugewiesen.", + "billing.ibanLearnStart" to "IBAN einlernen", + "billing.ibanLearnStep1" to "IBAN-Lernen: bitte auf die erste zu nutzende IBAN-Box klicken.", + "billing.ibanLearnStep2" to "IBAN-Lernen: bitte auf die letzte zu nutzende IBAN-Box klicken.", + "billing.ibanWithoutCountry" to "ohne Länderkennung", + "billing.locationText" to "Ort", + "billing.mappingEditorHint" to "Feld auswählen, dann in die PDF klicken. Die Position wird direkt übernommen.", + "billing.mappingEditorTitle" to "Felder im PDF per Klick zuweisen", + "billing.mappingField" to "Feld", + "billing.mappingHint" to "Koordinaten je Feld einmalig anpassen und speichern. Diese Positionen werden bei „PDF erzeugen“ verwendet.", + "billing.mappingPreviewError" to "PDF-Vorschau für Mapping konnte nicht geladen werden.", + "billing.mappingSaved" to "Mapping wurde gespeichert.", + "billing.mappingSaveError" to "Mapping konnte nicht gespeichert werden.", + "billing.mappingSuggested" to "Auto-Vorschläge wurden eingetragen. Bitte prüfen und feinjustieren.", + "billing.mappingSuggestError" to "Auto-Vorschläge konnten nicht ermittelt werden.", + "billing.mappingTitle" to "Positions-Mapping", + "billing.monthFrom" to "Monat von", + "billing.monthTo" to "Monat bis", + "billing.noRuns" to "Noch keine Abrechnungen vorhanden.", + "billing.noSessionsInRange" to "Keine Trainingseinheiten im gewählten Zeitraum gefunden.", + "billing.noTemplates" to "Noch keine Vorlagen vorhanden.", + "billing.omitField" to "nicht angeben", + "billing.openMappingEditor" to "Mapping per Klick", + "billing.resetIbanBoxes" to "IBAN-Boxen reset", + "billing.runSection" to "Abrechnungslauf", + "billing.runsTitle" to "Bisherige Abrechnungen", + "billing.sameAccountCheckbox" to "Gleiches Konto wie letzte Abrechnung", + "billing.saveMapping" to "Mapping speichern", + "billing.selfRecipientName" to "Eigener Name", + "billing.sessionHours" to "Stunden", + "billing.sessionLabel" to "Bezeichner", + "billing.sessionTime" to "Zeit", + "billing.step1" to "Schritt 1: Vorlage hinterlegen", + "billing.step2" to "Schritt 2: Abrechnungslauf anlegen", + "billing.step3" to "Schritt 3: PDF erzeugen und herunterladen", + "billing.subtitle" to "Erstelle deine eigene monatliche Übungsstunden-Abrechnung.", + "billing.template" to "Vorlage", + "billing.templateDescription" to "Beschreibung", + "billing.templateName" to "Vorlagenname", + "billing.templatePdf" to "PDF-Vorlage", + "billing.templateSection" to "Vorlage hochladen", + "billing.title" to "Abrechnung", + "billing.uploadTemplate" to "Vorlage speichern", + "club.accessDenied" to "无权访问此俱乐部。", + "club.accessRequested" to "已提交访问申请。", + "club.accessRequestFailed" to "无法提交访问申请。", + "club.accessRequestPending" to "已申请访问此俱乐部。请稍候。", + "club.create" to "创建俱乐部", + "club.createTitle" to "创建俱乐部", + "club.diary" to "训练日记", + "club.errorLoadingRequests" to "加载待处理申请时出错", + "club.load" to "加载", + "club.members" to "成员", + "club.mobileSelectHint" to "请先选择一个俱乐部,才能在智能手机上使用该应用。", + "club.name" to "俱乐部名称", + "club.new" to "新俱乐部", + "club.noAccess" to "您尚未获得此俱乐部的访问权限。", + "club.openAccessRequests" to "待处理的访问申请", + "club.openRequests" to "待处理的访问申请", + "club.requestAccess" to "申请访问权限", + "club.select" to "选择俱乐部", + "club.selectPlaceholder" to "选择俱乐部...", + "club.title" to "俱乐部", + "club.trainingDiary" to "训练日记", + "clubSettings.associationMemberNumber" to "Verbands-Mitgliedsnummer", + "clubSettings.associationMemberNumberPlaceholder" to "z. B. 12-3456", + "clubSettings.autoFetchRankings" to "Ranglisten automatisch abrufen", + "clubSettings.greetingHint" to "Dieser Text erscheint im Reiter \"Begrüßung\" des Spielberichtsbogens.", + "clubSettings.greetingPlaceholder" to "Begrüßungstext für Heimspiele...", + "clubSettings.greetingText" to "Begrüßungstext", + "clubSettings.guestPlayers" to "Spieler und Doppel Gastmannschaft", + "clubSettings.guestTeam" to "Name Gastmannschaft", + "clubSettings.homePlayers" to "Spieler und Doppel Heimmannschaft", + "clubSettings.homeTeam" to "Name Heimmannschaft", + "clubSettings.loadFailed" to "Einstellungen konnten nicht geladen werden", + "clubSettings.memberDataQuality" to "Datenqualität Mitglieder", + "clubSettings.memberDataQualityHint" to "Diese Felder zählen auf der Mitgliederseite als nötig. Alle Felder bleiben weiterhin eingebbar.", + "clubSettings.myTischtennisFedNickname" to "Verbandskürzel", + "clubSettings.myTischtennisFedNicknamePlaceholder" to "z. B. HeTTV", + "clubSettings.myTischtennisRankings" to "myTischtennis TTR/QTTR-Ranglisten", + "clubSettings.myTischtennisRankingsHint" to "Automatischer Abruf der Vereins-Rangliste für TTR- und QTTR-Updates der Mitglieder.", + "clubSettings.noClubSelected" to "Bitte wählen Sie zuerst einen Verein aus.", + "clubSettings.placeholders" to "Platzhalter", + "clubSettings.rankingsUsesAssociationNumber" to "Die Vereinsnummer für den Ranglisten-Abruf entspricht der Verbands-Mitgliedsnummer oben.", + "clubSettings.requireCity" to "Ort nötig", + "clubSettings.requireEmail" to "E-Mail-Adresse nötig", + "clubSettings.requirePhone" to "Telefonnummer nötig", + "clubSettings.requirePostalCode" to "PLZ nötig", + "clubSettings.requireStreet" to "Straße nötig", + "clubSettings.save" to "Speichern", + "clubSettings.saved" to "Gespeichert", + "clubSettings.saveFailed" to "Speichern fehlgeschlagen", + "clubSettings.settings" to "Einstellungen", + "clubSettings.title" to "Vereins-Einstellungen", + "clubSettings.trainingGroups" to "Trainingsgruppen", + "clubSettings.trainingTimes" to "Trainingszeiten", + "common.actions" to "操作", + "common.active" to "活跃", + "common.add" to "添加", + "common.all" to "全部", + "common.apply" to "应用", + "common.back" to "返回", + "common.cancel" to "取消", + "common.choose" to "选择", + "common.clear" to "清除", + "common.close" to "关闭", + "common.confirm" to "确认", + "common.create" to "创建", + "common.date" to "日期", + "common.days" to "天", + "common.delete" to "删除", + "common.description" to "描述", + "common.details" to "详情", + "common.disabled" to "已禁用", + "common.download" to "下载", + "common.edit" to "编辑", + "common.enabled" to "已启用", + "common.filter" to "筛选", + "common.hours" to "小时", + "common.in" to "后", + "common.inactive" to "非活跃", + "common.loading" to "加载中...", + "common.min" to "分", + "common.minutes" to "分钟", + "common.months" to "个月", + "common.move" to "移动", + "common.name" to "名称", + "common.new" to "新建", + "common.next" to "下一步", + "common.no" to "否", + "common.ok" to "确定", + "common.optional" to "可选", + "common.period" to "期间", + "common.previous" to "上一步", + "common.refresh" to "重新加载", + "common.remove" to "移除", + "common.required" to "必填", + "common.reset" to "重置", + "common.save" to "保存", + "common.saved" to "已保存", + "common.saving" to "正在保存...", + "common.search" to "搜索", + "common.select" to "选择", + "common.status" to "状态", + "common.submit" to "提交", + "common.time" to "时间", + "common.today" to "今天", + "common.type" to "类型", + "common.update" to "更新", + "common.view" to "查看", + "common.weeks" to "周", + "common.years" to "年", + "common.yes" to "是", + "courtDrawing.cancel" to "Abbrechen", + "courtDrawing.durationMinutes" to "Dauer (Minuten)", + "courtDrawing.durationText" to "Dauer (Text)", + "courtDrawing.durationTextPlaceholder" to "z.B. 2x5", + "courtDrawing.group" to "Gruppe", + "courtDrawing.ok" to "OK", + "courtDrawing.selectGroup" to "Gruppe auswählen...", + "courtDrawing.title" to "Tischtennis-Übung konfigurieren", + "courtDrawingRender.animationRunning" to "Animation läuft...", + "courtDrawingRender.startAnimation" to "Animation starten", + "courtDrawingTool.addStroke" to "添加击球", + "courtDrawingTool.backhand" to "反手", + "courtDrawingTool.codeLabel" to "缩写", + "courtDrawingTool.completeFirstStroke" to "完成第一拍", + "courtDrawingTool.completeFirstStrokeHint" to "请选择起始位置、击球面、旋转和目标,随后会显示图示。", + "courtDrawingTool.configureExercise" to "配置练习", + "courtDrawingTool.counterSpin" to "逆旋转", + "courtDrawingTool.forehand" to "正手", + "courtDrawingTool.noAdditionalStrokes" to "尚未添加后续击球。", + "courtDrawingTool.preview" to "预览", + "courtDrawingTool.previewHint" to "第一拍设置完整后将显示图示。", + "courtDrawingTool.service" to "发球:", + "courtDrawingTool.serviceTitle" to "发球", + "courtDrawingTool.sidespin" to "侧旋", + "courtDrawingTool.sideUnderspin" to "侧下旋", + "courtDrawingTool.spin" to "旋转:", + "courtDrawingTool.startLeft" to "左", + "courtDrawingTool.startMiddle" to "中", + "courtDrawingTool.startRight" to "右", + "courtDrawingTool.stepAdditionalStrokes" to "4. 后续击球", + "courtDrawingTool.stepAdditionalStrokesHint" to "可选地将后续来球添加为列表。", + "courtDrawingTool.stepFirstStroke" to "2. 第一拍", + "courtDrawingTool.stepFirstStrokeHint" to "设置第一球的击球面和旋转。", + "courtDrawingTool.stepStartPosition" to "1. 起始位置", + "courtDrawingTool.stepStartPositionHint" to "练习从哪个发球位置开始?", + "courtDrawingTool.stepTarget" to "3. 目标", + "courtDrawingTool.stepTargetHint" to "选择第一拍的目标区域。", + "courtDrawingTool.strokeSide" to "击球面", + "courtDrawingTool.strokeTypeBlock" to "挡球", + "courtDrawingTool.strokeTypeChopDefense" to "削球防守", + "courtDrawingTool.strokeTypeCounter" to "反击", + "courtDrawingTool.strokeTypeFlip" to "挑打", + "courtDrawingTool.strokeTypeLabel" to "击球方式", + "courtDrawingTool.strokeTypeLobDefense" to "高吊防守", + "courtDrawingTool.strokeTypePush" to "搓球", + "courtDrawingTool.strokeTypeSmash" to "扣杀", + "courtDrawingTool.strokeTypeTopspin" to "上旋", + "courtDrawingTool.targetBackhandHalfLong" to "反手半长", + "courtDrawingTool.targetBackhandLong" to "反手长", + "courtDrawingTool.targetBackhandShort" to "反手短", + "courtDrawingTool.targetForehandHalfLong" to "正手半长", + "courtDrawingTool.targetForehandLong" to "正手长", + "courtDrawingTool.targetForehandShort" to "正手短", + "courtDrawingTool.targetMiddleHalfLong" to "中路半长", + "courtDrawingTool.targetMiddleLong" to "中路长", + "courtDrawingTool.targetMiddleShort" to "中路短", + "courtDrawingTool.targetPosition" to "目标位置:", + "courtDrawingTool.targetPositionLabel" to "目标位置", + "courtDrawingTool.title" to "乒乓球练习示意图", + "courtDrawingTool.titleLabel" to "标题", + "courtDrawingTool.topspin" to "上旋", + "courtDrawingTool.toTarget" to "到", + "courtDrawingTool.underspin" to "下旋", + "createClub.clubExists" to "Der Verein existiert bereits.", + "createClub.clubName" to "Name des Vereins:", + "createClub.create" to "Verein anlegen", + "createClub.nameRequired" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "createClub.title" to "Verein anlegen", + "createClub.unknownError" to "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", + "csvImport.cancel" to "Abbrechen", + "csvImport.import" to "Importieren", + "csvImport.title" to "Spielplan importieren", + "csvImport.uploadCsvFile" to "CSV-Datei hochladen", + "dialogExamples.composableConfirmDetails" to "Dies ist ein Beispiel für das useConfirm Composable.", + "dialogExamples.composableConfirmMessage" to "Möchten Sie fortfahren?", + "dialogExamples.composableConfirmTitle" to "useConfirm Beispiel", + "dialogExamples.composableDialogText" to "Dieser Dialog verwendet das useDialog Composable.", + "dialogExamples.composableDialogText2" to "Das macht die Verwaltung einfacher!", + "dialogExamples.composableDialogTitle" to "Dialog mit useDialog Composable", + "dialogExamples.composableUsage" to "Composable-Verwendung", + "dialogExamples.confirmDeleteMessage" to "Möchten Sie diesen Eintrag wirklich löschen?", + "dialogExamples.confirmDeleteTitle" to "Löschen bestätigen", + "dialogExamples.confirmDialogs" to "Bestätigungs-Dialoge", + "dialogExamples.confirmWarningDetails" to "Diese Aktion kann nicht rückgängig gemacht werden.", + "dialogExamples.confirmWarningMessage" to "Sind Sie sicher, dass Sie fortfahren möchten?", + "dialogExamples.error" to "Fehler", + "dialogExamples.errorDetails" to "Bitte versuchen Sie es später erneut.", + "dialogExamples.errorMessage" to "Ein Fehler ist aufgetreten.", + "dialogExamples.fullscreenModal" to "Fullscreen Modal", + "dialogExamples.fullscreenModalText" to "Dies ist ein Fullscreen-Dialog.", + "dialogExamples.fullscreenModalText2" to "Er nimmt fast den gesamten Bildschirm ein (90vw x 90vh).", + "dialogExamples.fullscreenModalTitle" to "Fullscreen Modal Dialog", + "dialogExamples.info" to "Info", + "dialogExamples.infoDialogs" to "Informations-Dialoge", + "dialogExamples.infoMessage" to "Dies ist eine Informationsmeldung.", + "dialogExamples.information" to "Information", + "dialogExamples.largeModal" to "Großer Modal", + "dialogExamples.largeModalText" to "Dies ist ein großer modaler Dialog.", + "dialogExamples.largeModalText2" to "Er bietet mehr Platz für Inhalte.", + "dialogExamples.largeModalTitle" to "Großer Modal Dialog", + "dialogExamples.minimizedDialogs" to "Minimierte Dialoge:", + "dialogExamples.modalDialogs" to "Modale Dialoge", + "dialogExamples.multipleDialogs" to "Mehrere Dialoge", + "dialogExamples.nonModalDialog" to "Nicht-modaler Dialog", + "dialogExamples.nonModalDialogs" to "Nicht-modale Dialoge", + "dialogExamples.nonModalText" to "Dies ist ein nicht-modaler Dialog.", + "dialogExamples.nonModalText2" to "Sie können ihn verschieben und mehrere gleichzeitig öffnen!", + "dialogExamples.nonModalTitle" to "Nicht-modaler Dialog", + "dialogExamples.scrollArea" to "Scroll-Bereich für viel Inhalt...", + "dialogExamples.secondNonModalText" to "Noch ein nicht-modaler Dialog!", + "dialogExamples.secondNonModalTitle" to "Zweiter nicht-modaler Dialog", + "dialogExamples.simpleModal" to "Einfacher Modal", + "dialogExamples.simpleModalText" to "Dies ist ein einfacher modaler Dialog mit mittlerer Größe.", + "dialogExamples.simpleModalText2" to "Klicken Sie außerhalb oder auf das X, um zu schließen.", + "dialogExamples.simpleModalTitle" to "Einfacher Modal Dialog", + "dialogExamples.success" to "Erfolg", + "dialogExamples.successDetails" to "Alle Änderungen wurden gespeichert.", + "dialogExamples.successMessage" to "Der Vorgang wurde erfolgreich abgeschlossen!", + "dialogExamples.title" to "Dialog-Beispiele", + "dialogExamples.useConfirmExample" to "useConfirm Beispiel", + "dialogExamples.useDialogExample" to "useDialog Beispiel", + "dialogExamples.warning" to "Warnung", + "dialogExamples.warningDetails" to "Einige Felder sind möglicherweise nicht vollständig ausgefüllt.", + "dialogExamples.warningMessage" to "Bitte beachten Sie folgende Hinweise.", + "dialogManager.close" to "Schließen", + "dialogManager.minimize" to "Minimieren", + "dialogManager.noMinimizedDialogs" to "Keine minimierten Dialoge", + "dialogs.confirm.cancel" to "Abbrechen", + "dialogs.confirm.ok" to "OK", + "dialogs.confirm.title" to "Bestätigung", + "dialogs.info.ok" to "OK", + "dialogs.info.title" to "Information", + "diary.activeTrainingDay" to "当前训练日", + "diary.activities" to "活动", + "diary.activity" to "活动", + "diary.activityDrawing" to "活动图示", + "diary.activityImage" to "活动图片", + "diary.activityNotFound" to "未找到活动。请从列表中选择。", + "diary.activityOrTimeblock" to "活动 / 时间块", + "diary.activityPlaceholder" to "活动", + "diary.activityRequired" to "请输入活动。", + "diary.addActivity" to "添加活动", + "diary.addGroup" to "添加分组", + "diary.addGroupActivity" to "添加分组活动", + "diary.addGroupButton" to "+ 分组", + "diary.addTimeblock" to "时间块", + "diary.all" to "全部", + "diary.applySuggestion" to "应用建议", + "diary.assignParticipants" to "分配参与者", + "diary.assignParticipantsForGroupActivity" to "为分组活动分配参与者", + "diary.assignShort" to "分配", + "diary.bookAccident" to "记录事故", + "diary.confirmDelete" to "确认删除", + "diary.confirmDeleteDate" to "确定要删除此日期吗?", + "diary.confirmDeleteDateDetails" to "所有相关数据也将一并删除。", + "diary.confirmDeleteGroup" to "确定要删除分组“{name}”吗?", + "diary.createDate" to "创建日期", + "diary.createDrawing" to "创建练习图", + "diary.createGroups" to "创建分组", + "diary.createNew" to "新建", + "diary.createNewDate" to "创建新日期", + "diary.date" to "日期", + "diary.dateCannotBeDeleted" to "无法删除该日期", + "diary.dateCannotBeDeletedDetails" to "仍有内容存在(训练计划、参与者、活动、事故或备注)。", + "diary.dateNoLongerCurrent" to "所选日期已不是最新状态。请重试。", + "diary.delete" to "删除", + "diary.deleteDate" to "删除日期", + "diary.deleteGroup" to "删除分组", + "diary.duration" to "时长", + "diary.durationExampleLong" to "例如:2x7 或 3*5", + "diary.durationExampleShort" to "例如:2x7", + "diary.durationMinutes" to "时长(分)", + "diary.editActivity" to "编辑活动", + "diary.editGroupActivity" to "编辑分组活动", + "diary.editTrainingTimes" to "编辑训练时间", + "diary.errorCreatingActivity" to "创建活动时出错", + "diary.errorCreatingGroups" to "创建分组时出错", + "diary.errorDeletingGroup" to "删除分组时出错", + "diary.errorLoadingPredefinedActivities" to "加载预定义活动时出错", + "diary.errorMarkingForm" to "标记会员表格时出错", + "diary.errorOccurred" to "发生错误。请重试。", + "diary.existingGroups" to "现有分组", + "diary.filterAbsent" to "缺席", + "diary.filterAll" to "全部", + "diary.filterExcused" to "已请假", + "diary.filterPresent" to "出席", + "diary.filterTest" to "试用", + "diary.formHandedOver" to "会员表格已交付", + "diary.formMarkedAsHandedOver" to "已将会员表格标记为已交付", + "diary.freeActivities" to "自由活动", + "diary.gallery" to "成员图库", + "diary.galleryCreating" to "正在生成图库…", + "diary.group" to "分组...", + "diary.groupDeletedSuccessfully" to "分组已成功删除。", + "diary.groupManagement" to "分组管理", + "diary.groupsCreated" to "已成功创建 {count} 个分组。", + "diary.groupsLabel" to "分组", + "diary.groupsSection" to "分组", + "diary.leader" to "负责人", + "diary.min" to "分", + "diary.minutes" to "分钟", + "diary.mustCreateAtLeastTwoGroups" to "首次创建时至少需要 2 个分组。", + "diary.nextAppointment" to "下一个时间", + "diary.noActiveTrainingDay" to "未选择训练日。", + "diary.noEntries" to "没有条目", + "diary.noFreeActivitiesYet" to "尚未记录自由活动。", + "diary.noParticipants" to "该训练日没有参与者。", + "diary.numberOfGroups" to "分组数量", + "diary.oneGroupAdded" to "已成功添加 1 个分组。", + "diary.openPlanItems" to "{count} 项未完成", + "diary.openPlanItemsLabel" to "计划状态", + "diary.overallActivity" to "整体活动", + "diary.participants" to "参与者", + "diary.participantStatusCancelled" to "已取消", + "diary.participantStatusExcused" to "已请假", + "diary.participantStatusNone" to "无状态", + "diary.planActivitiesCount" to "计划活动", + "diary.planAddHint" to "可通过上方操作添加新的计划项目。", + "diary.planEmptyState" to "训练计划中还没有任何内容。", + "diary.quickAdd" to "+ 快速添加", + "diary.searchParticipants" to "搜索参与者", + "diary.selectGroup" to "选择分组...", + "diary.selectGroupAndActivity" to "请选择一个分组并输入活动。", + "diary.selectParticipantAndNote" to "请选择一位参与者并输入备注文本。", + "diary.selectTags" to "选择标签", + "diary.selectTrainingGroup" to "选择训练组", + "diary.selectTrainingGroupPlaceholder" to "请选择...", + "diary.showImage" to "显示图片/图示", + "diary.skipSuggestion" to "不使用建议继续", + "diary.standardActivities" to "标准活动", + "diary.standardActivityAddError" to "无法添加标准活动。", + "diary.standardDurationShort" to "分", + "diary.startTime" to "开始时间", + "diary.statusEmpty" to "该训练日仍为空。", + "diary.statusInProgress" to "该训练日已部分准备。", + "diary.statusOpenShort" to "未完成", + "diary.statusReady" to "时间和训练计划已维护完成。", + "diary.statusReadyShort" to "已就绪", + "diary.suggestion" to "建议", + "diary.timeblock" to "时间块", + "diary.timeblocksCount" to "时间块", + "diary.title" to "训练日记", + "diary.today" to "今天", + "diary.trainingDayAsPDF" to "将训练日下载为 PDF", + "diary.trainingDayAsPDFShort" to "训练日 PDF", + "diary.trainingDayChecklist" to "完成检查", + "diary.trainingDaySection" to "训练日", + "diary.trainingDaySummaryPdfShort" to "参与者概览 PDF", + "diary.trainingEnd" to "训练结束", + "diary.trainingPlan" to "训练计划", + "diary.trainingPlanAsPDF" to "将训练计划导出为 PDF", + "diary.trainingPlanPdfShort" to "流程计划 PDF", + "diary.trainingStart" to "训练开始", + "diary.trainingTimesUpdated" to "训练时间已成功更新。", + "diary.trainingWindow" to "训练时间段", + "diary.trainingWindowUnset" to "尚未设置", + "diary.unassignedPlanItems" to "{count} 项未分配", + "diary.updateTimes" to "更新时间", + "errors.ERROR_ACTIVITY_IMAGE_DELETE_FAILED" to "Fehler beim Löschen des Bildes", + "errors.ERROR_BAD_REQUEST" to "Ungültige Anfrage.", + "errors.ERROR_CLUB_ALREADY_EXISTS" to "Der Verein existiert bereits.", + "errors.ERROR_CLUB_NAME_REQUIRED" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NAME_TOO_SHORT" to "Bitte gib dem Verein einen aussagekräftigen Namen.", + "errors.ERROR_CLUB_NOT_FOUND" to "Verein nicht gefunden.", + "errors.ERROR_DIARY_ACTIVITY_PARTICIPANTS_UPDATE_FAILED" to "Fehler beim Aktualisieren der Aktivitäts-Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_ALL_PARTICIPANTS_FAILED" to "Fehler beim Zuordnen aller Teilnehmer", + "errors.ERROR_DIARY_ASSIGN_GROUP_FAILED" to "Fehler beim Zuordnen der Gruppe", + "errors.ERROR_DIARY_DATE_NOT_FOUND" to "Datum nicht gefunden.", + "errors.ERROR_DIARY_DATE_UPDATED" to "Datum war nicht (mehr) vorhanden. Die Datums-Auswahl wurde aktualisiert. Bitte erneut versuchen.", + "errors.ERROR_DIARY_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Gruppenzuordnung", + "errors.ERROR_DIARY_IMAGE_LOAD_FAILED" to "Bild konnte nicht geladen werden.", + "errors.ERROR_DIARY_MEMBER_CREATE_FAILED" to "Fehler beim Erstellen des Mitglieds", + "errors.ERROR_DIARY_NO_EXERCISE_DATA" to "Keine Übungsdaten erhalten", + "errors.ERROR_DIARY_NO_PARTICIPANTS" to "Keine Teilnehmer für diesen Trainingstag vorhanden.", + "errors.ERROR_DIARY_PARTICIPANT_ASSIGN_FAILED" to "Teilnehmer konnte nicht zugeordnet werden.", + "errors.ERROR_DIARY_PARTICIPANT_GROUP_ASSIGNMENT_UPDATE_FAILED" to "Fehler beim Aktualisieren der Teilnehmer-Gruppenzuordnung", + "errors.ERROR_DIARY_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs.", + "errors.ERROR_DIARY_STATS_LOAD_FAILED" to "Fehler beim Laden der Statistiken.", + "errors.ERROR_FORBIDDEN" to "Zugriff verweigert.", + "errors.ERROR_GROUP_ALREADY_EXISTS" to "Eine Gruppe mit diesem Namen existiert bereits.", + "errors.ERROR_GROUP_CANNOT_RENAME_PRESET" to "Vorgaben-Gruppen können nicht umbenannt werden.", + "errors.ERROR_GROUP_INVALID_PRESET_TYPE" to "Ungültiger Preset-Typ.", + "errors.ERROR_GROUP_NAME_REQUIRED" to "Bitte geben Sie einen Gruppennamen ein.", + "errors.ERROR_GROUP_NOT_FOUND" to "Gruppe nicht gefunden.", + "errors.ERROR_INTERNAL_SERVER_ERROR" to "Ein interner Serverfehler ist aufgetreten.", + "errors.ERROR_INVALID_PASSWORD" to "Ungültiges Passwort.", + "errors.ERROR_LOG_NOT_FOUND" to "Log-Eintrag nicht gefunden.", + "errors.ERROR_LOGIN_FAILED" to "Login fehlgeschlagen.", + "errors.ERROR_MEMBER_ALREADY_EXISTS" to "Mitglied existiert bereits.", + "errors.ERROR_MEMBER_FIRSTNAME_REQUIRED" to "Vorname ist erforderlich.", + "errors.ERROR_MEMBER_LASTNAME_REQUIRED" to "Nachname ist erforderlich.", + "errors.ERROR_MEMBER_NOT_FOUND" to "Mitglied nicht gefunden.", + "errors.ERROR_MEMBER_TRANSFER_BULK_FAILED" to "Fehler bei der Bulk-Übertragung: {message}", + "errors.ERROR_MYTISCHTENNIS_ACCOUNT_NOT_LINKED" to "Kein myTischtennis-Account verknüpft.", + "errors.ERROR_MYTISCHTENNIS_CAPTCHA_REQUIRED" to "CAPTCHA erforderlich. MyTischtennis verwendet jetzt ein CAPTCHA beim Login. Bitte loggen Sie sich einmal direkt auf mytischtennis.de ein, um das CAPTCHA zu lösen, oder kontaktieren Sie den Support.", + "errors.ERROR_MYTISCHTENNIS_INVALID_PASSWORD" to "Ungültiges myTischtennis-Passwort.", + "errors.ERROR_MYTISCHTENNIS_LOGIN_FAILED" to "myTischtennis-Login fehlgeschlagen. Bitte überprüfen Sie Ihre Zugangsdaten.", + "errors.ERROR_MYTISCHTENNIS_NO_PASSWORD_SAVED" to "Kein Passwort gespeichert und Session abgelaufen. Bitte Passwort eingeben.", + "errors.ERROR_MYTISCHTENNIS_PASSWORD_NOT_SAVED" to "Kein Passwort gespeichert. Bitte geben Sie Ihr Passwort ein.", + "errors.ERROR_MYTISCHTENNIS_SESSION_EXPIRED" to "Session abgelaufen. Bitte erneut einloggen.", + "errors.ERROR_MYTISCHTENNIS_USER_NOT_FOUND" to "myTischtennis-Benutzer nicht gefunden.", + "errors.ERROR_NOT_FOUND" to "Nicht gefunden.", + "errors.ERROR_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "Fehler beim Hochladen der PDF.", + "errors.ERROR_SESSION_EXPIRED" to "Sitzung abgelaufen.", + "errors.ERROR_TEAM_LINK_TO_LEAGUE_REQUIRED" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "errors.ERROR_TEAM_NOT_LINKED_TO_LEAGUE" to "Dieses Team ist keiner Liga zugeordnet.", + "errors.ERROR_TEAM_PDF_LOAD_FAILED" to "Fehler beim Laden des PDFs", + "errors.ERROR_TEAM_STATS_LOAD_FAILED" to "Statistiken konnten nicht geladen werden.", + "errors.ERROR_TOURNAMENT_CLASS_NAME_REQUIRED" to "Bitte geben Sie einen Klassennamen ein!", + "errors.ERROR_TOURNAMENT_NO_DATE" to "Kein Turnierdatum vorhanden!", + "errors.ERROR_TOURNAMENT_NO_PARTICIPANTS" to "Keine Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NO_TRAINING_DAY" to "Kein Trainingstag für {date} gefunden!", + "errors.ERROR_TOURNAMENT_NO_VALID_PARTICIPANTS" to "Keine gültigen Teilnehmer im Trainingstag für dieses Datum gefunden!", + "errors.ERROR_TOURNAMENT_NOT_FOUND" to "Turnier nicht gefunden.", + "errors.ERROR_TOURNAMENT_PDF_GENERATION_FAILED" to "Fehler beim Generieren des PDFs", + "errors.ERROR_TOURNAMENT_SELECT_FIRST" to "Bitte wählen Sie zuerst ein Turnier aus.", + "errors.ERROR_TRAINING_STATS_LOAD_FAILED" to "Fehler beim Laden der Trainings-Statistik", + "errors.ERROR_UNAUTHORIZED" to "Nicht autorisiert.", + "errors.ERROR_UNKNOWN_ERROR" to "Ein unbekannter Fehler ist aufgetreten.", + "errors.ERROR_USER_NOT_FOUND" to "Benutzer nicht gefunden.", + "errors.ERROR_VALIDATION_FAILED" to "Validierung fehlgeschlagen.", + "errors.SUCCESS_DIARY_GROUP_ASSIGNMENT_UPDATED" to "Gruppenzuordnung aktualisiert", + "errors.SUCCESS_DIARY_MEMBER_CREATED" to "Mitglied \"{name}\" wurde erfolgreich erstellt und hinzugefügt!", + "errors.SUCCESS_OFFICIAL_TOURNAMENT_PDF_UPLOAD" to "PDF erfolgreich hochgeladen.", + "home.authenticatedFeatures.keepDiary" to "Trainingstagebuch führen", + "home.authenticatedFeatures.keepDiaryDesc" to "Dokumentiere Trainingsaktivitäten, Teilnehmer, Aktivitäten und Notizen für jeden Trainingstag.", + "home.authenticatedFeatures.manageMembers" to "Mitglieder verwalten", + "home.authenticatedFeatures.manageMembersDesc" to "Verwalte deine Vereinsmitglieder, erstelle Trainingsgruppen und behalte den Überblick über alle Teilnehmer.", + "home.authenticatedFeatures.manageTournaments" to "Turniere verwalten", + "home.authenticatedFeatures.manageTournamentsDesc" to "Erstelle interne und offene Turniere, importiere offizielle Turniere und verwalte Teilnahmen.", + "home.authenticatedFeatures.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.authenticatedFeatures.myTischtennisIntegrationDesc" to "Synchronisiere automatisch Spielergebnisse und Statistiken mit MyTischtennis.de.", + "home.authenticatedFeatures.pdfExport" to "PDF-Export", + "home.authenticatedFeatures.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.authenticatedFeatures.statistics" to "Statistiken & Auswertungen", + "home.authenticatedFeatures.statisticsDesc" to "Erhalte detaillierte Trainings- und Teilnahmeübersichten sowie Aktivitätsstatistiken.", + "home.authenticatedFeatures.teamManagement" to "Team-Management", + "home.authenticatedFeatures.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.authenticatedFeatures.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.authenticatedFeatures.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.faq" to "Häufige Fragen", + "home.faqFree.answer" to "Ja, du kannst kostenlos starten. Erweiterungen können später folgen.", + "home.faqFree.question" to "Ist die Nutzung kostenlos?", + "home.faqInstallation.answer" to "Nein, es handelt sich um eine Web‑Anwendung. Du nutzt sie direkt im Browser – auf Desktop, Tablet und Smartphone.", + "home.faqInstallation.question" to "Benötige ich eine Installation?", + "home.faqMyTischtennis.answer" to "Ja, nach der Einrichtung synchronisiert sich die Anwendung automatisch mit MyTischtennis.de und importiert Spielergebnisse und Statistiken.", + "home.faqMyTischtennis.question" to "Funktioniert die MyTischtennis-Integration automatisch?", + "home.faqPermissions.answer" to "Es gibt vier Rollen: Admin, Trainer, Mannschaftsführer und Mitglied. Jede Rolle hat spezifische Berechtigungen, die individuell angepasst werden können.", + "home.faqPermissions.question" to "Wie funktioniert das Berechtigungssystem?", + "home.faqPrivacy.answer" to "Wir setzen auf Datensparsamkeit, transparente Freigaben, rollenbasierte Zugriffe und vollständiges Aktivitätsprotokoll. Die Anwendung ist DSGVO‑konform.", + "home.faqPrivacy.question" to "Wie steht es um den Datenschutz?", + "home.faqTournaments.answer" to "Du kannst interne Turniere, offene Turniere und offizielle Turniere (z.B. von Verbänden) verwalten. Offizielle Turniere können importiert werden.", + "home.faqTournaments.question" to "Welche Turnierarten werden unterstützt?", + "home.faqTrainingGroups.answer" to "Ja, du kannst Trainingsgruppen anlegen, Trainingszeiten definieren und Mitglieder den Gruppen zuordnen. Das Trainingstagebuch schlägt automatisch passende Gruppen und Zeiten vor.", + "home.faqTrainingGroups.question" to "Kann ich Trainingsgruppen und -zeiten verwalten?", + "home.features.activityLog" to "Aktivitätsprotokoll", + "home.features.activityLogDesc" to "Vollständiges Logging aller Aktionen für Transparenz und Nachvollziehbarkeit.", + "home.features.keepDiary" to "Trainingstagebuch führen", + "home.features.keepDiaryDesc" to "Dokumentiere Inhalte, Umfang und Anwesenheiten jeder Einheit – nachvollziehbar und strukturiert.", + "home.features.manageMembers" to "Mitglieder verwalten", + "home.features.manageMembersDesc" to "Erstelle Mitgliedsprofile, bilde Gruppen und halte Kontakt‑ und Freigabestände aktuell.", + "home.features.myTischtennisIntegration" to "MyTischtennis-Integration", + "home.features.myTischtennisIntegrationDesc" to "Automatische Synchronisation mit MyTischtennis.de für Spielergebnisse und Statistiken.", + "home.features.officialTournaments" to "Offizielle Turniere", + "home.features.officialTournamentsDesc" to "Importiere und verwalte offizielle Turniere, verwalte Teilnahmen und Ergebnisse.", + "home.features.organizeSchedules" to "Spielpläne organisieren", + "home.features.organizeSchedulesDesc" to "Plane Spiele, Turniere und Veranstaltungen inklusive Gruppen, Runden und Ergebnissen.", + "home.features.pdfExport" to "PDF-Export", + "home.features.pdfExportDesc" to "Exportiere Trainingstage als PDF mit Teilnehmern, Aktivitäten und Statistiken.", + "home.features.permissionSystem" to "Berechtigungssystem", + "home.features.permissionSystemDesc" to "Rollenbasierte Zugriffe (Admin, Trainer, Mannschaftsführer, Mitglied) mit individuellen Berechtigungen.", + "home.features.predefinedActivities" to "Vordefinierte Aktivitäten", + "home.features.predefinedActivitiesDesc" to "Nutze Vorlagen für wiederkehrende Übungen und beschleunige deine Dokumentation.", + "home.features.security" to "Sicherheit & DSGVO", + "home.features.securityDesc" to "Datenschutzfreundliche Architektur, Freigaben durch Mitglieder und transparente Zugriffe.", + "home.features.statistics" to "Statistiken & Auswertung", + "home.features.statisticsDesc" to "Erhalte Trainings‑ und Teilnahmeübersichten, erkenne Entwicklung und plane gezielt.", + "home.features.teamManagement" to "Team-Management", + "home.features.teamManagementDesc" to "Verwalte Mannschaften, Ligen, Spielpläne und Ergebnisse für deinen Verein.", + "home.features.trainingGroups" to "Trainingsgruppen & Zeiten", + "home.features.trainingGroupsDesc" to "Organisiere Trainingsgruppen, definiere Trainingszeiten und verwalte Gruppenzuordnungen.", + "home.forWhom" to "Für wen ist das TrainingsTagebuch?", + "home.forWhomText" to "Das TrainingsTagebuch ist die umfassende Plattform für Vereine, Abteilungen und Trainerteams. Es vereint Mitgliederverwaltung mit Trainingsgruppen und -zeiten, detailliertes Trainingstagebuch, umfassende Turnierorganisation (interne, offene und offizielle Turniere), Team-Management mit Liga-Integration, MyTischtennis-Synchronisation, aussagekräftige Statistiken und Auswertungen sowie ein flexibles Berechtigungssystem in einer modernen Web‑Anwendung. Durch klare Rollen (Admin, Trainer, Mannschaftsführer, Mitglied) und individuelle Berechtigungen behalten Verantwortliche die Kontrolle, während Mitglieder selbstbestimmt mitwirken können. Ideal für Mannschafts‑, Racket‑ und Individualsportarten – vom Nachwuchs bis zum Leistungsbereich. DSGVO‑konform mit transparenten Freigaben und vollständigem Aktivitätsprotokoll.", + "home.haveAccount" to "Ich habe schon einen Account", + "home.heroFeatures.memberManagement" to "Mitglieder- und Gruppenverwaltung", + "home.heroFeatures.myTischtennis" to "MyTischtennis-Integration", + "home.heroFeatures.statistics" to "Statistiken & Auswertungen", + "home.heroFeatures.teamManagement" to "Team-Management & Ligen", + "home.heroFeatures.tournaments" to "Turniere (intern, offen, offiziell)", + "home.heroFeatures.trainingDiary" to "Trainingstagebuch & Dokumentation", + "home.heroFeatures.trainingGroups" to "Trainingsgruppen & Trainingszeiten", + "home.heroSubtitle" to "Das TrainingsTagebuch ist die umfassende Lösung für Vereine: Mitgliederverwaltung, Trainingsgruppen, Trainingszeiten, Trainingstagebuch, Turnierorganisation, Team-Management, MyTischtennis-Integration, Statistiken und mehr – DSGVO‑konform und einfach zu bedienen.", + "home.heroTitle" to "Vereinsverwaltung, Trainingsplanung und Turniere – alles an einem Ort", + "home.howItWorks" to "So funktioniert es", + "home.registerNow" to "Jetzt kostenlos registrieren", + "home.rolesAndPrivacy" to "Rollen, Berechtigungen & DSGVO", + "home.startFree" to "Kostenlos starten", + "home.step1.description" to "Lege kostenlos einen Account an und aktiviere ihn per E‑Mail.", + "home.step1.title" to "Registrieren", + "home.step2.description" to "Erstelle deinen Verein, lade Mitglieder ein und richte Gruppen ein.", + "home.step2.title" to "Verein anlegen", + "home.step3.description" to "Plane Termine, dokumentiere Trainings und verfolge Fortschritte.", + "home.step3.title" to "Planen & dokumentieren", + "home.welcome" to "Willkommen im TrainingsTagebuch", + "home.welcomeBack" to "Herzlich Willkommen zurück! Du bist erfolgreich eingeloggt.", + "home.whatCanYouDo" to "Was kannst du mit dem TrainingsTagebuch machen?", + "imageDialog.close" to "Schließen", + "imageDialog.noImageAvailable" to "Kein Bild verfügbar", + "imageDialog.title" to "Bild", + "imageViewer.camera" to "Kamera", + "imageViewer.delete" to "Löschen", + "imageViewer.deleteImage" to "Bild löschen", + "imageViewer.nextImage" to "Nächstes Bild", + "imageViewer.noImageAvailable" to "Kein Bild verfügbar", + "imageViewer.previousImage" to "Vorheriges Bild", + "imageViewer.rotateLeft" to "90° links drehen", + "imageViewer.rotateRight" to "90° rechts drehen", + "imageViewer.selectFiles" to "Dateien auswählen", + "imageViewer.setAsPrimary" to "Als Hauptbild festlegen", + "imageViewer.setAsPrimaryButton" to "Als Hauptbild setzen", + "imprint.contact" to "Kontakt", + "imprint.serviceProvider" to "Diensteanbieter", + "imprint.title" to "Impressum", + "languages.de" to "Deutsch (德语)", + "languages.de-CH" to "Schwiizerdütsch (瑞士德语)", + "languages.en-AU" to "English (英语(AU))", + "languages.en-GB" to "English (英语(GB))", + "languages.en-US" to "English (英语(US))", + "languages.es" to "Español (西班牙语)", + "languages.fil" to "Filipino (菲律宾语)", + "languages.fr" to "Français (法语)", + "languages.it" to "Italiano (意大利语)", + "languages.ja" to "日本語 (日语)", + "languages.pl" to "Polski (波兰语)", + "languages.th" to "ไทย (泰语)", + "languages.tl" to "Tagalog (他加禄语)", + "languages.zh" to "中文", + "legal.backToHome" to "Zur Startseite", + "legal.imprint" to "Impressum", + "legal.privacyPolicy" to "Datenschutzerklärung", + "logs.actions" to "Aktionen", + "logs.all" to "Alle", + "logs.apiRequests" to "API-Requests", + "logs.applyFilters" to "Filter anwenden", + "logs.backend" to "Backend", + "logs.clearFilters" to "Zurücksetzen", + "logs.cronJobs" to "Cron-Jobs", + "logs.details" to "Details", + "logs.displayed" to "angezeigt", + "logs.displayedLabel" to "Angezeigt", + "logs.error" to "Fehler", + "logs.errorLabel" to "Fehler", + "logs.errorLoading" to "Fehler beim Laden der Logs", + "logs.executionTime" to "Ausführungszeit", + "logs.from" to "Von", + "logs.httpMethod" to "HTTP-Methode", + "logs.justNow" to "gerade eben", + "logs.loadingLogs" to "Lade Logs...", + "logs.logDetails" to "Log-Details", + "logs.logDetailsLabels.error" to "Fehler", + "logs.logDetailsLabels.executionTime" to "Ausführungszeit", + "logs.logDetailsLabels.ip" to "IP", + "logs.logDetailsLabels.method" to "Methode", + "logs.logDetailsLabels.path" to "Pfad", + "logs.logDetailsLabels.requestBody" to "Request Body", + "logs.logDetailsLabels.responseBody" to "Response Body", + "logs.logDetailsLabels.schedulerJob" to "Scheduler-Job", + "logs.logDetailsLabels.status" to "Status", + "logs.logDetailsLabels.time" to "Zeit", + "logs.logDetailsLabels.type" to "Typ", + "logs.logType" to "Log-Typ", + "logs.logTypeLabels.api_request" to "API-Request", + "logs.logTypeLabels.cron_job" to "Cron-Job", + "logs.logTypeLabels.manual" to "Manuell", + "logs.logTypeLabels.scheduler" to "Scheduler", + "logs.manual" to "Manuelle Ausführungen", + "logs.method" to "Methode", + "logs.minutesAgo" to "vor {n} Minuten", + "logs.mytischtennis" to "myTischtennis", + "logs.next" to "Nächste", + "logs.of" to "von", + "logs.ownBackend" to "Eigenes Backend", + "logs.page" to "Seite", + "logs.path" to "Pfad", + "logs.pathPlaceholder" to "z.B. /api/diary", + "logs.previous" to "Vorherige", + "logs.recordsFound" to "Datensätze gefunden", + "logs.scheduler" to "Scheduler", + "logs.secondsAgo" to "vor {n} Sekunden", + "logs.status" to "Status", + "logs.subtitle" to "Übersicht über alle API-Requests, Responses und Ausführungen", + "logs.successfullyLoaded" to "Erfolgreich geladen", + "logs.time" to "Zeit", + "logs.title" to "System-Logs", + "logs.to" to "Bis", + "logs.total" to "Gesamt", + "logs.type" to "Typ", + "matchReport.analyzeNuscore" to "nuscore analysieren", + "matchReport.createLocalCopy" to "Lokale Kopie erstellen", + "matchReport.hideAnalyzer" to "Analyzer verstecken", + "matchReportApi.availablePlayers" to "Verfügbare Spieler", + "matchReportApi.bothTeamsAppeared" to "Beide Mannschaften angetreten", + "matchReportApi.clickToRemove" to "Klicken zum Entfernen", + "matchReportApi.completed" to "Abgeschlossen", + "matchReportApi.completion" to "Abschluss", + "matchReportApi.endTime" to "Endzeit", + "matchReportApi.errorLoading" to "Fehler beim Laden der Daten", + "matchReportApi.general" to "Allgemein", + "matchReportApi.greeting" to "Begrüßung", + "matchReportApi.guestLineup" to "Aufstellung Gast", + "matchReportApi.guestPin" to "PIN Gastverein", + "matchReportApi.guestTeamNotAppeared" to "Gastmannschaft {team} nicht angetreten", + "matchReportApi.homeLineup" to "Aufstellung Heim", + "matchReportApi.homePin" to "PIN Heimverein", + "matchReportApi.homeTeamNotAppeared" to "Heimmannschaft {team} nicht angetreten", + "matchReportApi.loading" to "Lade Spielberichtsdaten...", + "matchReportApi.noLineupData" to "Keine Aufstellungsdaten verfügbar", + "matchReportApi.notAppeared" to "Nicht angetreten", + "matchReportApi.pending" to "Ausstehend", + "matchReportApi.pinInput" to "PIN-Eingabe", + "matchReportApi.playSystem" to "Spielsystem", + "matchReportApi.rank" to "Rang", + "matchReportApi.result" to "Ergebniserfassung", + "matchReportApi.retry" to "Erneut versuchen", + "matchReportApi.selectedPlayers" to "Ausgewählte Spieler", + "matchReportApi.setCurrentTime" to "Aktuelle Zeit setzen", + "matchReportApi.signLineup" to "Aufstellung signieren", + "matchReportApi.startTime" to "Startzeit", + "matchReportApi.status" to "Status", + "matchReportApi.vs" to "vs", + "matchReportHeaderActions.copied" to "Kopiert!", + "matchReportHeaderActions.copyError" to "Fehler beim Kopieren der PIN", + "matchReportHeaderActions.copyPin" to "PIN kopieren", + "matchReportHeaderActions.copyPinTitle" to "PIN in Zwischenablage kopieren", + "matchReportHeaderActions.insertPin" to "PIN einfügen", + "matchReportHeaderActions.insertPinTitle" to "PIN automatisch einfügen", + "matchReportHeaderActions.noPinAvailable" to "Keine PIN verfügbar", + "memberActivities.activity" to "Übung", + "memberActivities.all" to "Alle", + "memberActivities.close" to "Schließen", + "memberActivities.dates" to "Daten", + "memberActivities.frequency" to "Häufigkeit", + "memberActivities.last3Months" to "Letzte 3 Monate", + "memberActivities.last4Weeks" to "Letzte 4 Wochen", + "memberActivities.last6Months" to "Letztes halbes Jahr", + "memberActivities.lastYear" to "Letztes Jahr", + "memberActivities.loading" to "Lade Übungen...", + "memberActivities.more" to "weitere", + "memberActivities.noActivities" to "Keine Übungen im gewählten Zeitraum gefunden.", + "memberActivities.period" to "Zeitraum", + "memberActivities.showLess" to "weniger anzeigen", + "memberActivities.title" to "Übungen von", + "memberGallery.imageSize" to "Bildgröße", + "memberGallery.loading" to "Galerie wird geladen…", + "memberGallery.noImage" to "Kein Bild", + "memberGallery.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "memberGallery.title" to "Mitglieder-Galerie", + "memberGallery.titleWithDate" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als Teilnehmer hinzuzufügen", + "memberNotes.add" to "Hinzufügen", + "memberNotes.memberImage" to "Mitgliedsbild", + "memberNotes.newNote" to "Neue Notiz", + "memberNotes.notes" to "Notizen", + "memberNotes.phone" to "Telefon-Nr.", + "memberNotes.selectTags" to "Tags auswählen", + "memberNotes.tags" to "Tags", + "memberNotes.title" to "Notizen für", + "members.actions" to "操作", + "members.active" to "活跃", + "members.activeMembers" to "活跃成员", + "members.addEmail" to "添加电子邮箱地址", + "members.addGroup" to "添加分组...", + "members.addPhone" to "添加电话号码", + "members.address" to "地址", + "members.adultReleaseApproved" to "已批准参加成人组", + "members.adultReserveApproved" to "成人组替补", + "members.adults" to "成人(20岁以上)", + "members.age" to "Age", + "members.ageFromPlaceholder" to "从", + "members.ageGroup" to "年龄组", + "members.ageRange" to "年龄范围", + "members.ageToPlaceholder" to "到", + "members.batchFormsMarkedEmpty" to "There are no unverified forms in the current selection.", + "members.batchFormsMarkedSuccess" to "Marked {count} form(s) as verified.", + "members.batchMarkedRegularEmpty" to "There are no trial members in the current selection.", + "members.batchMarkedRegularSuccess" to "Marked {count} trial member(s) as regular.", + "members.batchPartialFailure" to "{success} succeeded, {failed} failed.", + "members.birthdate" to "出生日期", + "members.bulkActions" to "批量操作", + "members.camera" to "相机", + "members.change" to "更改", + "members.city" to "城市", + "members.clearFields" to "清空字段", + "members.clearFilters" to "重置筛选", + "members.clickTtRequestAction" to "Request click-TT eligibility", + "members.clickTtRequestConfirm" to "Start the automated Click-TT request for {name}?", + "members.clickTtRequestError" to "The Click-TT request could not be submitted.", + "members.clickTtRequestFailedLog" to "Click-TT request failed", + "members.clickTtRequestHint" to "The request is processed automatically by the backend Click-TT workflow.", + "members.clickTtRequestPending" to "Click-TT request in progress", + "members.clickTtRequestPendingShort" to "Pending ...", + "members.clickTtRequestShort" to "Click-TT", + "members.clickTtRequestSuccess" to "Please sign in to click-tt and submit the request there.", + "members.clickTtRequestTitle" to "Start Click-TT request", + "members.clickTtSubmitted" to "Click-TT submitted", + "members.closeEditor" to "Close editor", + "members.composeEmail" to "Compose email", + "members.contact" to "Contact", + "members.copyContactSummary" to "Copy contact summary", + "members.copyContactSummarySuccess" to "Contact summary copied to clipboard.", + "members.copyEmails" to "Copy emails", + "members.copyEmailsEmpty" to "There are no email addresses in the current selection.", + "members.copyEmailsSuccess" to "Email list copied to clipboard.", + "members.copyPhones" to "Copy phones", + "members.copyPhonesEmpty" to "There are no phone numbers in the current selection.", + "members.copyPhonesSuccess" to "Phone list copied to clipboard.", + "members.create" to "创建", + "members.createNewMember" to "创建新成员", + "members.dataIssueAddress" to "缺少地址", + "members.dataIssueBirthdate" to "缺少出生日期", + "members.dataIssueCity" to "缺少城市", + "members.dataIssueEmail" to "缺少电子邮箱地址", + "members.dataIssueGender" to "性别未明确", + "members.dataIssuePhone" to "缺少电话号码", + "members.dataIssuePostalCode" to "缺少邮政编码", + "members.dataIssueStreet" to "缺少街道", + "members.dataIssueTrainingGroup" to "缺少训练组", + "members.dataQuality" to "数据质量", + "members.dataQualityComplete" to "数据完整", + "members.deactivateMember" to "停用成员", + "members.deactivateMemberConfirm" to "确定要停用“{name}”吗?", + "members.deactivateMemberTitle" to "停用成员", + "members.deleteImageConfirm" to "Do you really want to delete this image?", + "members.deleteImageTitle" to "Delete image", + "members.editHint" to "Click a row to open the editor.", + "members.editMember" to "编辑成员", + "members.editorAssignTrainingGroupHint" to "请至少分配一个训练组。", + "members.editorCreateHint" to "Create a new member and capture contact details directly.", + "members.editorEditHint" to "Edit the details for {name}.", + "members.editorRecommendedEntry" to "建议填写", + "members.emailAddress" to "电子邮箱地址", + "members.emailAddressShort" to "邮箱", + "members.emails" to "电子邮箱地址", + "members.errorAddingToGroup" to "添加到分组时出错", + "members.errorDeactivatingMember" to "停用成员时出错", + "members.errorDeletingImage" to "无法删除图片", + "members.errorLoadingImage" to "加载图片时出错", + "members.errorLoadingMembers" to "Failed to load the member list.", + "members.errorLoadingTrainingParticipations" to "加载训练参与记录时出错", + "members.errorMarkingForm" to "标记表格时出错。", + "members.errorRemovingFromGroup" to "从分组移除时出错", + "members.errorRemovingTestMembership" to "移除试用会员资格时出错。", + "members.errorRotatingImage" to "旋转图片时出错", + "members.errorSavingMember" to "保存成员时出错", + "members.errorSettingPrimaryImage" to "无法设置主图片", + "members.errorTransfer" to "转移时出错", + "members.errorUpdatingRatings" to "更新 TTR/QTTR 时出错", + "members.errorUploadingImage" to "无法上传图片", + "members.exercises" to "练习", + "members.exportCsv" to "Export CSV", + "members.exportCsvFile" to "member-selection.csv", + "members.exportEmails" to "Email", + "members.exportMembersSelected" to "members currently selected", + "members.exportPhones" to "Phone", + "members.exportPreview" to "Export preview", + "members.exportPreviewEmpty" to "No members in the current selection", + "members.exportPreviewNames" to "Preview", + "members.exportReachableByEmail" to "with email address", + "members.exportReachableByPhone" to "with phone number", + "members.firstName" to "名", + "members.formHandedOver" to "会员表格已交付", + "members.formMarkedAsHandedOver" to "已将会员表格标记为已交付。", + "members.gender" to "性别", + "members.genderDiverse" to "其他", + "members.genderFemale" to "女", + "members.genderMale" to "男", + "members.genderUnknown" to "未知", + "members.generatePhoneList" to "生成电话列表", + "members.groupPhotoCrop" to "处理集体照", + "members.groupPhotoCropSaved" to "已从集体照保存会员照片。", + "members.image" to "图片", + "members.imageDeleted" to "图片已删除。", + "members.imageInternet" to "图片(网络)", + "members.imagePreview" to "成员图片预览", + "members.imageUpdated" to "图片已更新", + "members.inactive" to "非活跃", + "members.inactiveMembers" to "非活跃成员", + "members.j11" to "J11", + "members.j13" to "J13", + "members.j15" to "J15", + "members.j17" to "J17(17岁及以下)", + "members.j19" to "J19", + "members.j9" to "J9", + "members.lastName" to "姓", + "members.lastTrainingFilter" to "最近训练", + "members.lastTrainingFilterHasDate" to "有记录日期", + "members.lastTrainingFilterHint" to "表格:年龄组列显示当前赛季。悬停行可查看两赛季、最近训练及参与次数(若有)。", + "members.lastTrainingFilterNoDate" to "无最近训练", + "members.lastTrainingFilterNotInTraining" to "标记「不再参加训练」", + "members.loadingMembers" to "Loading members...", + "members.m11" to "M11", + "members.m13" to "M13", + "members.m15" to "M15", + "members.m19" to "M19", + "members.m9" to "M9", + "members.markFormReceived" to "Mark form verified", + "members.markFormsForSelection" to "Verify forms for selection", + "members.markRegular" to "Mark regular", + "members.markRegularForSelection" to "Mark selection regular", + "members.memberDeactivated" to "成员已停用", + "members.memberDetails" to "Member details", + "members.memberFormHandedOver" to "会员表格已交付", + "members.memberImage" to "成员图片", + "members.memberImages" to "成员图片", + "members.memberInfo" to "成员信息", + "members.memberScope" to "Member scope", + "members.missingTtrHistoryId" to "未保存 myTischtennis ID", + "members.name" to "姓名", + "members.newMember" to "新成员", + "members.noAddressShort" to "No address", + "members.noEmailShort" to "No email", + "members.noGroupsAssigned" to "未分配任何分组", + "members.noGroupsAvailable" to "没有可用分组", + "members.noOpenTasks" to "No open tasks", + "members.noPhoneShort" to "No phone", + "members.notes" to "备注", + "members.noTestMembership" to "不再是试用会员", + "members.notInTrainingTooltip" to "No participation for {weeks} training weeks", + "members.onlyActiveMembers" to "仅导出活跃成员", + "members.openTasks" to "Open tasks", + "members.parent" to "家长", + "members.parentFallback" to "Parent", + "members.parentName" to "姓名(例如:母亲、父亲)", + "members.phoneList" to "电话列表.pdf", + "members.phoneListForSelection" to "Phone list for selection", + "members.phoneListSelectionFile" to "phone-list-selection.pdf", + "members.phoneNumber" to "电话号码", + "members.phoneNumberShort" to "电话", + "members.phones" to "电话号码", + "members.picsInInternetAllowed" to "允许在互联网上发布照片", + "members.postalCode" to "邮政编码", + "members.previewLastTraining" to "Last training", + "members.previewNoLastTraining" to "No participation yet", + "members.primary" to "主要", + "members.primaryImageUpdated" to "主图片已更新。", + "members.remove" to "移除", + "members.resultsVisible" to "members visible", + "members.rowTooltipSeparator" to "·", + "members.scopeActive" to "Active", + "members.scopeActiveDataIncomplete" to "活跃 + 数据不完整", + "members.scopeActiveTest" to "Active + trial", + "members.scopeAll" to "All", + "members.scopeDataIncomplete" to "数据不完整", + "members.scopeInactive" to "Inactive", + "members.scopeNeedsForm" to "Form unverified", + "members.scopeNotTraining" to "No longer training", + "members.scopeTest" to "Trial", + "members.search" to "Search", + "members.searchAndFilter" to "搜索和筛选", + "members.searchPlaceholder" to "Search by name, city, phone or email", + "members.selectFile" to "选择文件", + "members.showInactiveMembers" to "显示非活跃成员", + "members.showTrainingParticipationsColumn" to "显示「训练参与」列", + "members.showTtrHistory" to "显示 TTR 历史", + "members.sixOrMoreParticipations" to "训练参与 6 次或以上", + "members.sortAge" to "Age", + "members.sortBirthday" to "Birthday", + "members.sortBy" to "Sort by", + "members.sortFirstName" to "First name", + "members.sortLastName" to "Last name", + "members.sortLastTraining" to "Last training", + "members.sortOpenTasks" to "Open tasks", + "members.sortQttr" to "QTTR值", + "members.status" to "状态", + "members.street" to "街道", + "members.subtitle" to "Search, filter and edit members directly.", + "members.taskActionMarkRegular" to "Mark regular", + "members.taskActionRequest" to "Request", + "members.taskActionReview" to "打开", + "members.taskActionVerify" to "Verify", + "members.taskAssignTrainingGroup" to "分配训练组", + "members.taskCheckClickTt" to "Review Click-TT eligibility", + "members.taskCheckDataQuality" to "检查数据质量", + "members.taskCheckTrainingStatus" to "Review training status", + "members.taskReviewTrialStatus" to "Review trial status", + "members.taskVerifyForm" to "Verify form", + "members.testMember" to "试用", + "members.testMembers" to "试用成员", + "members.testMembership" to "试用会员", + "members.testMembershipRemoved" to "试用会员资格已移除。", + "members.threeOrMoreParticipations" to "训练参与 3 次或以上", + "members.title" to "成员", + "members.toggleSortDirection" to "Toggle sort direction", + "members.trainingGroups" to "训练组", + "members.trainingParticipations" to "训练参与次数", + "members.transferErrorTitle" to "Transfer error", + "members.transferMembers" to "转移成员", + "members.transferSuccessTitle" to "Transfer successful", + "members.ttAdult" to "成人(按截止日非青年)", + "members.ttAgeClassCol" to "年龄组(TT)", + "members.ttFilterGroupJ" to "男女孩(混合)", + "members.ttFilterGroupM" to "仅女孩", + "members.ttrQttr" to "TTR / QTTR", + "members.ttSeasonCurrentTag" to "当前", + "members.ttSeasonFilter" to "赛季(截止日)", + "members.ttSeasonNextTag" to "下一", + "members.ttStichtagHint" to "截止日1月1日(DTTB)。男孩:仅 J 组。女孩:J 与 M。", + "members.updateRatings" to "从 myTischtennis 更新 TTR/QTTR", + "members.updating" to "更新中...", + "members.visibleMembers" to "Visible members", + "members.wantsToPlay" to "想参加比赛", + "memberSelection.close" to "Schließen", + "memberSelection.deselectAll" to "Alle abwählen", + "memberSelection.generatePdf" to "PDF erzeugen", + "memberSelection.members" to "Mitglieder", + "memberSelection.noRecommendations" to "Keine passenden Empfehlungen gefunden.", + "memberSelection.recommendations" to "Empfehlungen", + "memberSelection.selectAll" to "Alle auswählen", + "memberSelection.title" to "Mitglieder auswählen", + "memberTransfer.additionalField" to "Zusätzliches Feld", + "memberTransfer.additionalFieldExample1" to "Zusätzliches Feld (z.B. client_id)", + "memberTransfer.additionalFieldExample2" to "Zusätzliches Feld (z.B. client_secret)", + "memberTransfer.analyzeAndImport" to "Template analysieren und importieren", + "memberTransfer.availablePlaceholders" to "Verfügbare Platzhalter", + "memberTransfer.bulkMode" to "Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)", + "memberTransfer.bulkModeActive" to "Bulk-Modus aktiv", + "memberTransfer.bulkModeActiveDescription" to "Das Template definiert das Format für ein einzelnes Mitglied. Die Mitglieder werden automatisch in ein Array gewrappt. Die äußere Struktur können Sie optional im \"Bulk-Wrapper-Template\" definieren (siehe unten).", + "memberTransfer.bulkModeHint" to "Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen.", + "memberTransfer.bulkWrapperDescription" to "Optional können Sie die äußere Struktur definieren, in die die Mitglieder-Array eingefügt wird. Verwenden Sie PLACEHOLDER_MEMBERS als Platzhalter für das Array der Mitglieder.", + "memberTransfer.bulkWrapperNote" to "Hinweis: Wenn kein Wrapper-Template angegeben wird, wird automatisch ein members-Array verwendet.", + "memberTransfer.bulkWrapperTemplate" to "Bulk-Wrapper-Template (optional)", + "memberTransfer.bulkWrapperWhat" to "Was ist ein Bulk-Wrapper-Template?", + "memberTransfer.configDeleted" to "Konfiguration erfolgreich gelöscht!", + "memberTransfer.configInfo" to "Konfigurieren Sie hier die Einstellungen für die Übertragung von Mitgliedern an externe Systeme. Diese Einstellungen werden vereinsspezifisch gespeichert.", + "memberTransfer.configSaved" to "Konfiguration erfolgreich gespeichert!", + "memberTransfer.confirmDelete" to "Konfiguration löschen", + "memberTransfer.confirmDeleteMessage" to "Möchten Sie die Konfiguration wirklich löschen?", + "memberTransfer.deleteConfig" to "Konfiguration löschen", + "memberTransfer.deleting" to "Lösche...", + "memberTransfer.errorDeleting" to "Fehler beim Löschen", + "memberTransfer.errorLoading" to "Fehler beim Laden der Konfiguration", + "memberTransfer.errorParsingTemplate" to "Fehler beim Parsen des Templates", + "memberTransfer.errorSaving" to "Fehler beim Speichern", + "memberTransfer.example" to "Beispiel", + "memberTransfer.exampleFormData" to "Beispiel für Form-Data Format", + "memberTransfer.exampleJson" to "Beispiel für JSON-Format (empfohlen)", + "memberTransfer.exampleXml" to "Beispiel für XML-Format", + "memberTransfer.formDataHint" to "Für Form-Data verwenden Sie ein einfaches Text-Template mit Zeilen im Format feldname=platzhalter:", + "memberTransfer.httpMethod" to "HTTP-Methode", + "memberTransfer.importTemplate" to "Template aus vollständigem Beispiel importieren", + "memberTransfer.importTemplateHint" to "Fügen Sie ein vollständiges Beispiel-Template (mit Beispiel-Mitgliedern) ein. Das System erkennt automatisch das Mitglied-Template und das Bulk-Wrapper-Template.", + "memberTransfer.importTemplatePlaceholder" to "Fügen Sie hier ein vollständiges Beispiel-Template ein, z.B.:\nLBRACE\n DQUOTEmembersDQUOTE: [\n LBRACE\n DQUOTEfirstNameDQUOTE: DQUOTEMaxDQUOTE,\n DQUOTElastNameDQUOTE: DQUOTEMustermannDQUOTE,\n DQUOTEemailDQUOTE: DQUOTEmaxATexample.comDQUOTE\n RBRACE\n ]\nRBRACE", + "memberTransfer.invalidJson" to "Bitte stellen Sie sicher, dass gültiges JSON verwendet wird.", + "memberTransfer.invalidTemplateFormat" to "Ungültiges Template-Format", + "memberTransfer.loadingConfig" to "Konfiguration wird geladen...", + "memberTransfer.loginConfiguration" to "Login-Konfiguration", + "memberTransfer.loginData" to "Login-Daten", + "memberTransfer.loginEndpointHint" to "Optional: Relativer Pfad zum Login-Endpoint (z.B. /api/auth/login)", + "memberTransfer.loginEndpointPath" to "Login-Endpoint Pfad", + "memberTransfer.loginEndpointPlaceholder" to "/api/auth/login", + "memberTransfer.loginFormat" to "Login-Format", + "memberTransfer.membersArray" to "Mitglieder-Array", + "memberTransfer.password" to "Passwort", + "memberTransfer.passwordEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "memberTransfer.placeholderHint" to "Klicken Sie auf einen Platzhalter, um ihn an der aktuellen Cursor-Position einzufügen:", + "memberTransfer.placeholders.address" to "Kombinierte Adresse", + "memberTransfer.placeholders.addressDesc" to "Straße und Ort kombiniert", + "memberTransfer.placeholders.birthDate" to "Geburtsdatum", + "memberTransfer.placeholders.birthDateAlt" to "Geburtsdatum (alt)", + "memberTransfer.placeholders.birthDateAltDesc" to "Geburtsdatum im Format YYYY-MM-DD (alternative Bezeichnung)", + "memberTransfer.placeholders.birthDateDesc" to "Geburtsdatum im Format YYYY-MM-DD", + "memberTransfer.placeholders.city" to "Ort", + "memberTransfer.placeholders.cityDesc" to "Wohnort", + "memberTransfer.placeholders.email" to "E-Mail-Adresse", + "memberTransfer.placeholders.emailDesc" to "E-Mail-Adresse des Mitglieds", + "memberTransfer.placeholders.firstName" to "Vorname", + "memberTransfer.placeholders.firstNameDesc" to "Vorname des Mitglieds", + "memberTransfer.placeholders.fullName" to "Vollständiger Name", + "memberTransfer.placeholders.fullNameDesc" to "Vorname und Nachname kombiniert", + "memberTransfer.placeholders.gender" to "Geschlecht", + "memberTransfer.placeholders.genderDesc" to "Geschlecht des Mitglieds", + "memberTransfer.placeholders.lastName" to "Nachname", + "memberTransfer.placeholders.lastNameDesc" to "Nachname des Mitglieds", + "memberTransfer.placeholders.phone" to "Telefonnummer", + "memberTransfer.placeholders.phoneDesc" to "Telefonnummer des Mitglieds", + "memberTransfer.placeholders.qttr" to "QTTR-Wert", + "memberTransfer.placeholders.qttrDesc" to "QTTR-Wert des Mitglieds", + "memberTransfer.placeholders.street" to "Straße", + "memberTransfer.placeholders.streetDesc" to "Straße und Hausnummer", + "memberTransfer.placeholders.ttr" to "TTR-Wert", + "memberTransfer.placeholders.ttrDesc" to "TTR-Wert des Mitglieds", + "memberTransfer.save" to "Speichern", + "memberTransfer.saving" to "Speichere...", + "memberTransfer.serverBaseUrl" to "Server-Basis-URL", + "memberTransfer.serverBaseUrlHint" to "Basis-URL des Servers (z.B. https://example.com)", + "memberTransfer.serverBaseUrlPlaceholder" to "https://example.com", + "memberTransfer.serverConfiguration" to "Server-Konfiguration", + "memberTransfer.templateDescription" to "Das Template definiert das Format, in dem die Mitgliederdaten an das externe System übertragen werden. Verwenden Sie Platzhalter wie PLACEHOLDER_FIRSTNAME, um die Daten automatisch zu ersetzen.", + "memberTransfer.templateImported" to "Template erfolgreich importiert!", + "memberTransfer.templateImportedBulk" to "Mitglied-Template erkannt, Bulk-Modus aktiviert.", + "memberTransfer.templateImportedDetails" to "Mitglied- und Bulk-Wrapper-Template wurden erkannt und ausgefüllt.", + "memberTransfer.templateImportedSingle" to "Einzelnes Mitglied-Template erkannt.", + "memberTransfer.templateTip" to "Tipp: Setzen Sie den Cursor an die gewünschte Stelle im Template und klicken Sie auf einen Platzhalter, um ihn einzufügen. Platzhalter werden beim Übertragen automatisch durch die tatsächlichen Mitgliederdaten ersetzt.", + "memberTransfer.templateWhat" to "Was ist ein Template?", + "memberTransfer.title" to "Mitgliederübertragung - Einstellungen", + "memberTransfer.transferConfiguration" to "Übertragungskonfiguration", + "memberTransfer.transferConfigurationTitle" to "Übertragungs-Konfiguration", + "memberTransfer.transferEndpointHint" to "Relativer Pfad zum Übertragungs-Endpoint (z.B. /api/members/bulk)", + "memberTransfer.transferEndpointPath" to "Übertragungs-Endpoint Pfad", + "memberTransfer.transferEndpointPlaceholder" to "/api/members/bulk", + "memberTransfer.transferFormat" to "Übertragungs-Format", + "memberTransfer.transferTemplate" to "Übertragungs-Template", + "memberTransfer.usernameEmail" to "Benutzername / Email", + "memberTransferDialog.additionalErrors" to "Weitere Fehler", + "memberTransferDialog.additionalFieldPlaceholder" to "Zusätzliches Feld (z.B. client_id)", + "memberTransferDialog.additionalFieldPlaceholderForm" to "Feldname: Wert (z.B. client_id: abc123)", + "memberTransferDialog.bulkImport" to "Bulk-Import", + "memberTransferDialog.cancel" to "Abbrechen", + "memberTransferDialog.editConfig" to "Konfiguration bearbeiten", + "memberTransferDialog.endpoint" to "Endpoint", + "memberTransferDialog.excludedMembers" to "Ausgeschlossene Mitglieder (fehlende Pflichtfelder)", + "memberTransferDialog.format" to "Format", + "memberTransferDialog.goToSettings" to "Zu den Einstellungen", + "memberTransferDialog.loadingConfig" to "Gespeicherte Konfiguration wird geladen...", + "memberTransferDialog.loginData" to "Login-Daten", + "memberTransferDialog.loginDataHint" to "Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.", + "memberTransferDialog.loginDataOverride" to "Login-Daten (optional überschreiben)", + "memberTransferDialog.loginDataOverrideHint" to "Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.", + "memberTransferDialog.method" to "Methode", + "memberTransferDialog.mode" to "Modus", + "memberTransferDialog.noConfigFound" to "Keine Konfiguration gefunden", + "memberTransferDialog.noConfigMessage" to "Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.", + "memberTransferDialog.passwordPlaceholder" to "Passwort (leer lassen für gespeichertes)", + "memberTransferDialog.server" to "Server", + "memberTransferDialog.single" to "Einzeln", + "memberTransferDialog.title" to "Mitglieder übertragen", + "memberTransferDialog.transfer" to "Übertragen", + "memberTransferDialog.transferConfiguration" to "Übertragungskonfiguration", + "memberTransferDialog.transferError" to "Fehler bei der Übertragung", + "memberTransferDialog.transferFailed" to "Übertragung fehlgeschlagen", + "memberTransferDialog.transferring" to "Übertrage...", + "memberTransferDialog.transferSuccess" to "{transferred} von {total} Mitgliedern erfolgreich übertragen.", + "memberTransferDialog.usernameEmail" to "Benutzername / Email", + "messages.cancel" to "取消", + "messages.confirm" to "确认", + "messages.error" to "错误", + "messages.fileTooLarge" to "Datei zu groß", + "messages.imageMaxSize" to "Das Bild darf maximal 5 MB groß sein.", + "messages.info" to "信息", + "messages.invalidFile" to "Ungültige Datei", + "messages.note" to "Hinweis", + "messages.pleaseSelectImageFile" to "Bitte wähle eine Bilddatei aus.", + "messages.recordsDisplayed" to "angezeigt", + "messages.recordsFound" to "Datensätze gefunden", + "messages.success" to "成功", + "messages.successfullyLoaded" to "Erfolgreich geladen", + "messages.warning" to "警告", + "mobile.active" to "Aktiv", + "mobile.add" to "Hinzufügen", + "mobile.appLoading" to "App wird geladen", + "mobile.cancel" to "Abbrechen", + "mobile.clubRequest" to "Zugriff anfragen", + "mobile.clubRequested" to "Angefragt", + "mobile.createDiaryEntry" to "Eintrag erstellen", + "mobile.deleteNote" to "Notiz löschen", + "mobile.deleteTag" to "Tag entfernen", + "mobile.editTimes" to "Zeiten bearbeiten", + "mobile.emailAndPasswordRequired" to "E-Mail und Passwort sind erforderlich", + "mobile.entry" to "Eintrag", + "mobile.inactive" to "Inaktiv", + "mobile.language" to "Sprache", + "mobile.lastTraining" to "Letztes Training", + "mobile.loginFailed" to "Login fehlgeschlagen", + "mobile.loginInProgress" to "Anmelden...", + "mobile.more" to "Mehr", + "mobile.new" to "Neu", + "mobile.newNote" to "Neue Notiz", + "mobile.newTag" to "Neuer Tag", + "mobile.noDiaryEntries" to "Noch keine Tagebuch-Einträge", + "mobile.noMembers" to "Keine Mitglieder gefunden", + "mobile.noResults" to "Keine Treffer", + "mobile.noTimes" to "Keine Zeiten", + "mobile.participationTop" to "Top Teilnahmen", + "mobile.refresh" to "Aktualisieren", + "mobile.requestedAccess" to "Angefragt", + "mobile.role" to "Rolle", + "mobile.saveEntry" to "Speichern", + "mobile.search" to "Suche", + "mobile.select" to "Auswählen", + "mobile.sessionCheck" to "Session prüfen", + "mobile.sessionCheckFailed" to "Session check fehlgeschlagen", + "mobile.sessionInvalid" to "Session ungültig", + "mobile.sessionValid" to "Session gültig", + "mobile.status" to "Status", + "mobile.user" to "Benutzer", + "myTischtennis.about" to "Über myTischtennis", + "myTischtennis.aboutFeatures.fetch" to "Wettkampfdaten direkt abrufen", + "myTischtennis.aboutFeatures.import" to "Automatisch Turnierdaten importieren", + "myTischtennis.aboutFeatures.sync" to "Spielerergebnisse synchronisieren", + "myTischtennis.aboutText" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennis.autoUpdates" to "Automatische Updates", + "myTischtennis.club" to "Verein (myTischtennis)", + "myTischtennis.deleteAccount" to "Account trennen", + "myTischtennis.disabled" to "Deaktiviert", + "myTischtennis.editAccount" to "Account bearbeiten", + "myTischtennis.enabled" to "Aktiviert", + "myTischtennis.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennis.lastFetch" to "Letzter Abruf", + "myTischtennis.lastLoginAttempt" to "Letzter Login-Versuch", + "myTischtennis.lastSuccessfulLogin" to "Letzter erfolgreicher Login", + "myTischtennis.leagueTables" to "Ligatabellen", + "myTischtennis.linkAccount" to "Account verknüpfen", + "myTischtennis.linkedAccount" to "Verknüpfter Account", + "myTischtennis.loadingStats" to "Lade Statistiken...", + "myTischtennis.matchResults" to "Spielergebnisse", + "myTischtennis.neverFetched" to "Noch nie abgerufen", + "myTischtennis.no" to "Nein", + "myTischtennis.noAccount" to "Kein myTischtennis-Account verknüpft.", + "myTischtennis.passwordNote" to "Hinweis: Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennis.passwordSaved" to "Passwort gespeichert", + "myTischtennis.passwordsEncrypted" to "Passwörter werden verschlüsselt gespeichert", + "myTischtennis.playerRatings" to "Spielerwertungen", + "myTischtennis.refreshStats" to "Statistiken aktualisieren", + "myTischtennis.testLogin" to "Erneut einloggen", + "myTischtennis.title" to "myTischtennis-Account", + "myTischtennis.updateHistory" to "Update-History", + "myTischtennis.yes" to "Ja", + "myTischtennisAccount.aboutDescription" to "Durch die Verknüpfung Ihres myTischtennis-Accounts können Sie:", + "myTischtennisAccount.aboutFeature1" to "Automatisch Turnierdaten importieren", + "myTischtennisAccount.aboutFeature2" to "Spielerergebnisse synchronisieren", + "myTischtennisAccount.aboutFeature3" to "Wettkampfdaten direkt abrufen", + "myTischtennisAccount.aboutHint" to "Das Speichern des Passworts ist optional. Wenn Sie es nicht speichern, werden Sie bei jeder Synchronisation nach dem Passwort gefragt.", + "myTischtennisAccount.aboutMyTischtennis" to "Über myTischtennis", + "myTischtennisAccount.accountSaved" to "myTischtennis-Account erfolgreich gespeichert", + "myTischtennisAccount.accountUnlinked" to "myTischtennis-Account erfolgreich getrennt", + "myTischtennisAccount.autoUpdates" to "Automatische Updates:", + "myTischtennisAccount.club" to "Verein (myTischtennis):", + "myTischtennisAccount.daysAgo" to "vor {count} Tagen", + "myTischtennisAccount.disabled" to "Deaktiviert", + "myTischtennisAccount.editAccount" to "Account bearbeiten", + "myTischtennisAccount.email" to "E-Mail:", + "myTischtennisAccount.enabled" to "Aktiviert", + "myTischtennisAccount.errorLoadingAccount" to "Fehler beim Laden des myTischtennis-Accounts", + "myTischtennisAccount.errorLoadingStatistics" to "Fehler beim Laden der Fetch-Statistiken", + "myTischtennisAccount.errorUnlinking" to "Fehler beim Trennen des Accounts", + "myTischtennisAccount.fetchStatistics" to "Datenabruf-Statistiken", + "myTischtennisAccount.hoursAgo" to "vor {count} Std.", + "myTischtennisAccount.justNow" to "Gerade eben", + "myTischtennisAccount.lastFetch" to "Letzter Abruf:", + "myTischtennisAccount.lastLoginAttempt" to "Letzter Login-Versuch:", + "myTischtennisAccount.lastSuccessfulLogin" to "Letzter erfolgreicher Login:", + "myTischtennisAccount.leagueTables" to "Ligatabellen", + "myTischtennisAccount.linkAccount" to "Account verknüpfen", + "myTischtennisAccount.linkedAccount" to "Verknüpfter Account", + "myTischtennisAccount.loading" to "Lade...", + "myTischtennisAccount.loadingStatistics" to "Lade Statistiken...", + "myTischtennisAccount.loginAgain" to "Erneut einloggen", + "myTischtennisAccount.loginFailed" to "Login fehlgeschlagen", + "myTischtennisAccount.loginSuccessful" to "Login erfolgreich! Verbindungsdaten aktualisiert.", + "myTischtennisAccount.matchResults" to "Spielergebnisse", + "myTischtennisAccount.minutesAgo" to "vor {count} Min.", + "myTischtennisAccount.never" to "Nie", + "myTischtennisAccount.neverFetched" to "Noch nie abgerufen", + "myTischtennisAccount.no" to "Nein", + "myTischtennisAccount.noAccountLinked" to "Kein myTischtennis-Account verknüpft.", + "myTischtennisAccount.passwordRequired" to "Passwort benötigt", + "myTischtennisAccount.passwordSaved" to "Passwort gespeichert:", + "myTischtennisAccount.playerRatings" to "Spielerwertungen", + "myTischtennisAccount.playersUpdated" to "Spieler aktualisiert", + "myTischtennisAccount.refreshStatistics" to "Statistiken aktualisieren", + "myTischtennisAccount.results" to "Ergebnisse", + "myTischtennisAccount.teams" to "Teams", + "myTischtennisAccount.title" to "myTischtennis-Account", + "myTischtennisAccount.unlinkAccount" to "Account trennen", + "myTischtennisAccount.unlinkAccountConfirm" to "Möchten Sie die Verknüpfung zum myTischtennis-Account wirklich trennen?", + "myTischtennisAccount.unlinkAccountTitle" to "Account trennen", + "myTischtennisAccount.updateHistory" to "Update-History", + "myTischtennisAccount.yes" to "Ja", + "myTischtennisAccount.yesterday" to "Gestern", + "myTischtennisDialog.appPassword" to "Ihr App-Passwort zur Bestätigung", + "myTischtennisDialog.appPasswordHint" to "Aus Sicherheitsgründen benötigen wir Ihr App-Passwort, um das myTischtennis-Passwort zu speichern.", + "myTischtennisDialog.appPasswordPlaceholder" to "Ihr Passwort für diese App", + "myTischtennisDialog.autoUpdateRatings" to "Automatische Update-Ratings aktivieren", + "myTischtennisDialog.autoUpdateRatingsHint" to "Täglich um 6:00 Uhr werden automatisch die neuesten Ratings von myTischtennis abgerufen. Erfordert gespeichertes Passwort.", + "myTischtennisDialog.autoUpdateWarning" to "Für automatische Updates muss das myTischtennis-Passwort gespeichert werden.", + "myTischtennisDialog.cancel" to "Abbrechen", + "myTischtennisDialog.editAccount" to "myTischtennis-Account bearbeiten", + "myTischtennisDialog.email" to "myTischtennis-E-Mail", + "myTischtennisDialog.emailPlaceholder" to "Ihre myTischtennis-E-Mail-Adresse", + "myTischtennisDialog.errorLogin" to "Fehler beim Einloggen", + "myTischtennisDialog.errorSaving" to "Fehler beim Speichern des Accounts", + "myTischtennisDialog.linkAccount" to "myTischtennis-Account verknüpfen", + "myTischtennisDialog.loadingLoginForm" to "Lade Login-Formular...", + "myTischtennisDialog.loggingIn" to "Logge ein...", + "myTischtennisDialog.login" to "Einloggen", + "myTischtennisDialog.password" to "myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholder" to "Ihr myTischtennis-Passwort", + "myTischtennisDialog.passwordPlaceholderKeep" to "Leer lassen um beizubehalten", + "myTischtennisDialog.save" to "Speichern", + "myTischtennisDialog.savePassword" to "myTischtennis-Passwort speichern", + "myTischtennisDialog.savePasswordHint" to "Wenn aktiviert, wird Ihr myTischtennis-Passwort verschlüsselt gespeichert, sodass automatische Synchronisationen möglich sind.", + "myTischtennisDialog.saving" to "Speichere...", + "myTischtennisHistory.close" to "Schließen", + "myTischtennisHistory.failed" to "Fehlgeschlagen", + "myTischtennisHistory.loading" to "Lade History...", + "myTischtennisHistory.noHistory" to "Noch keine automatischen Updates durchgeführt.", + "myTischtennisHistory.ratingsUpdated" to "Ratings aktualisiert", + "myTischtennisHistory.successful" to "Erfolgreich", + "myTischtennisHistory.title" to "Update-Ratings History", + "navigation.approvals" to "审批", + "navigation.backToHome" to "返回首页", + "navigation.billing" to "账单", + "navigation.clickTtAccount" to "HTTV / click-TT 账户", + "navigation.clickTtBrowser" to "HTTV / click-TT", + "navigation.clubSettings" to "俱乐部设置", + "navigation.clubTournaments" to "俱乐部锦标赛", + "navigation.competitions" to "比赛", + "navigation.dailyBusiness" to "日常事务", + "navigation.diary" to "日记", + "navigation.home" to "首页", + "navigation.login" to "登录", + "navigation.logout" to "退出登录", + "navigation.logs" to "系统日志", + "navigation.members" to "成员", + "navigation.memberTransfer" to "成员转移", + "navigation.myTischtennisAccount" to "myTischtennis账户", + "navigation.orders" to "订单", + "navigation.permissions" to "权限", + "navigation.personalSettings" to "个人设置", + "navigation.predefinedActivities" to "预定义活动", + "navigation.register" to "注册", + "navigation.schedule" to "赛程", + "navigation.settings" to "设置", + "navigation.statistics" to "训练统计", + "navigation.teamManagement" to "团队管理", + "navigation.tournamentParticipations" to "锦标赛参赛记录", + "navigation.tournaments" to "锦标赛", + "nuscoreAnalyzer.analysisComplete" to "✅ Analyse abgeschlossen: {count} Ressourcen gefunden", + "nuscoreAnalyzer.analyze" to "🔍 nuscore analysieren", + "nuscoreAnalyzer.analyzing" to "🔄 Analysiere...", + "nuscoreAnalyzer.createLocalCopy" to "🏗️ Lokale Kopie erstellen", + "nuscoreAnalyzer.creating" to "🏗️ Erstelle...", + "nuscoreAnalyzer.description" to "Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter", + "nuscoreAnalyzer.downloading" to "⬇️ Lade herunter...", + "nuscoreAnalyzer.downloadResources" to "Ressourcen herunterladen", + "nuscoreAnalyzer.foundResources" to "📋 Gefundene Ressourcen:", + "nuscoreAnalyzer.log" to "📝 Log:", + "nuscoreAnalyzer.pageLoaded" to "✅ nuscore-Seite geladen", + "nuscoreAnalyzer.startAnalysis" to "🔍 Starte nuscore-Analyse...", + "nuscoreAnalyzer.title" to "🔍 nuscore Analyzer", + "officialTournaments.action" to "Aktion", + "officialTournaments.age" to "Alter", + "officialTournaments.ageClassCompetition" to "Altersklasse/Wettbewerb", + "officialTournaments.ageClasses" to "Altersklassen:", + "officialTournaments.all" to "Alle", + "officialTournaments.birthDate" to "Geburtsdatum", + "officialTournaments.checkBoxToActivate" to "Checkbox aktivieren", + "officialTournaments.competition" to "Konkurrenz", + "officialTournaments.competitions" to "Konkurrenzen", + "officialTournaments.competitionTypes" to "Konkurrenztypen:", + "officialTournaments.cutoffDate" to "Stichtag:", + "officialTournaments.date" to "Datum", + "officialTournaments.dateTime" to "Termin:", + "officialTournaments.deadlineDate" to "Meldeschluss (Datum):", + "officialTournaments.deadlineOnline" to "Meldeschluss (Online):", + "officialTournaments.deadlines" to "Meldeschlüsse:", + "officialTournaments.deadlinesByAgeClass" to "Meldeschlüsse je AK:", + "officialTournaments.delete" to "Löschen", + "officialTournaments.editTitle" to "Titel bearbeiten", + "officialTournaments.eligible" to "Teilnahmeberechtigt", + "officialTournaments.entryFee" to "Startgeld", + "officialTournaments.entryFees" to "Teilnahmegebühren:", + "officialTournaments.events" to "Veranstaltungen", + "officialTournaments.finalRound" to "Endrunde:", + "officialTournaments.hasPlayed" to "Hat gespielt", + "officialTournaments.last12Months" to "Letzte 12 Monate", + "officialTournaments.last2Years" to "Letzte 2 Jahre", + "officialTournaments.last3Months" to "Letzte 3 Monate", + "officialTournaments.last6Months" to "Letzte 6 Monate", + "officialTournaments.markAsParticipated" to "Als teilgenommen markieren", + "officialTournaments.markAsRegistered" to "Als angemeldet markieren", + "officialTournaments.member" to "Mitglied", + "officialTournaments.name" to "Name", + "officialTournaments.noEvents" to "Noch keine Veranstaltungen vorhanden. Laden Sie ein PDF hoch, um zu beginnen.", + "officialTournaments.notInterested" to "Nicht interessiert", + "officialTournaments.openTo" to "Offen für:", + "officialTournaments.participants" to "Teilnehmer", + "officialTournaments.participantsPdf" to "Teilnehmer-PDF", + "officialTournaments.participated" to "Teilgenommen", + "officialTournaments.participations" to "Turnierbeteiligungen", + "officialTournaments.pdfForSelectedMembers" to "PDF für markierte Mitglieder", + "officialTournaments.placement" to "Platzierung", + "officialTournaments.preliminaryRound" to "Vorrunde:", + "officialTournaments.previousSeason" to "Vorherige Saison", + "officialTournaments.registered" to "Angemeldet", + "officialTournaments.resetStatus" to "Status zurücksetzen", + "officialTournaments.results" to "Ergebnisse", + "officialTournaments.savedEvents" to "Gespeicherte Veranstaltungen", + "officialTournaments.selectMembers" to "Mitglieder auswählen", + "officialTournaments.showCompetitions" to "Konkurrenzen anzeigen", + "officialTournaments.showEvents" to "Gespeicherte Veranstaltungen anzeigen", + "officialTournaments.showParticipants" to "Teilnehmer anzeigen", + "officialTournaments.showParticipations" to "Turnierbeteiligungen anzeigen", + "officialTournaments.showResults" to "Ergebnisse anzeigen", + "officialTournaments.startTime" to "Startzeit", + "officialTournaments.startTimes" to "Startzeiten:", + "officialTournaments.status" to "Status", + "officialTournaments.timeRange" to "Zeitraum:", + "officialTournaments.title" to "Titel:", + "officialTournaments.tournament" to "Turnier", + "officialTournaments.updatePlacementError" to "Fehler beim Aktualisieren der Platzierung", + "officialTournaments.updateStatusError" to "Fehler beim Aktualisieren des Status", + "officialTournaments.updateTitleError" to "Titel konnte nicht gespeichert werden.", + "officialTournaments.uploadError" to "Fehler beim Hochladen der PDF.", + "officialTournaments.uploadPdf" to "PDF hochladen", + "officialTournaments.venues" to "Austragungsorte:", + "officialTournaments.wantsToParticipate" to "Möchte teilnehmen", + "orders.addOrder" to "新建订单", + "orders.budget" to "Budget", + "orders.club" to "俱乐部", + "orders.cost" to "费用", + "orders.dateAutoHint" to "日期会自动设置,并且每次变更都会连同日期一起记录。", + "orders.errorLoading" to "无法加载订单。", + "orders.errorSaving" to "无法保存订单。", + "orders.filterAllClubs" to "所有俱乐部", + "orders.filterAllCompletionStates" to "Alle Abschlüsse", + "orders.filterAllStatuses" to "所有状态", + "orders.filterHandedOver" to "Artikel ausgehändigt", + "orders.filterPaid" to "Hat bezahlt", + "orders.globalSubtitle" to "可在此跨俱乐部查看和管理所有订单。", + "orders.globalTitle" to "所有俱乐部的订单", + "orders.history" to "历史", + "orders.item" to "商品", + "orders.itemPlaceholder" to "例如:球衣、卫衣或球拍套", + "orders.loading" to "正在加载订单...", + "orders.member" to "成员", + "orders.memberTitle" to "订单:{name}", + "orders.noOrdersGlobal" to "当前没有订单。", + "orders.noOrdersMember" to "该成员目前还没有订单。", + "orders.open" to "未结清", + "orders.orderDate" to "创建时间", + "orders.paid" to "已支付", + "orders.paidConfirmed" to "Hat bezahlt", + "orders.searchPlaceholder" to "按俱乐部、成员或商品搜索", + "orders.showCompleted" to "Abgeschlossene einblenden", + "orders.status" to "状态", + "orders.statusArrived" to "商品已到", + "orders.statusDate" to "最后变更", + "orders.statusHandedOver" to "商品已发放", + "orders.statusOrdered" to "已订购", + "orders.statusRequested" to "希望购买", + "orders.title" to "订单", + "pdfGenerator.activityTimeBlock" to "Aktivität / Zeitblock", + "pdfGenerator.allGames" to "Alle Spiele", + "pdfGenerator.alsoPlayable" to "Ebenfalls spielbar", + "pdfGenerator.birthDate" to "Geburtsdatum", + "pdfGenerator.clubLabel" to "俱乐部:", + "pdfGenerator.competition" to "Wettbewerb", + "pdfGenerator.competitionName" to "Konkurrenz", + "pdfGenerator.date" to "Datum:", + "pdfGenerator.diff" to "Diff", + "pdfGenerator.duration" to "Dauer (Min)", + "pdfGenerator.fee" to "Gebühr", + "pdfGenerator.generatedAt" to "生成时间:", + "pdfGenerator.group" to "Gruppe", + "pdfGenerator.groupLabel" to "Gruppe", + "pdfGenerator.groupMatrices" to "Gruppen-Matrizen", + "pdfGenerator.hallShoes" to "Hallenschuhe (dürfen auf Boden nicht abfärben)", + "pdfGenerator.hints" to "Hinweise:", + "pdfGenerator.informTrainer" to "Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren", + "pdfGenerator.leagueLabel" to "联赛:", + "pdfGenerator.lineupQttr" to "(Q)TTR", + "pdfGenerator.member" to "Mitglied:", + "pdfGenerator.nameFirstName" to "Name, Vorname", + "pdfGenerator.noActivities" to "Keine Aktivitäten für diesen Trainingstag erfasst.", + "pdfGenerator.noWhiteJersey" to "Kein weißes Trikot", + "pdfGenerator.officialTournament" to "Offizielles Turnier", + "pdfGenerator.oneHourBefore" to "Eine Stunde vor Beginn der Konkurrenz in der Halle sein", + "pdfGenerator.overallRanking" to "Gesamt-Ranking (K.O.-Runden)", + "pdfGenerator.periodLabel" to "报名阶段:", + "pdfGenerator.phoneList" to "Telefonliste - Aktive Mitglieder", + "pdfGenerator.phoneNumber" to "Telefon-Nr.", + "pdfGenerator.place" to "Platz", + "pdfGenerator.placement" to "Platzierung", + "pdfGenerator.plannedLeagueLabel" to "计划联赛:", + "pdfGenerator.player" to "Spieler", + "pdfGenerator.player1" to "Spieler 1", + "pdfGenerator.player2" to "Spieler 2", + "pdfGenerator.points" to "Punkte", + "pdfGenerator.recommendations" to "Empfehlungen", + "pdfGenerator.result" to "Ergebnis", + "pdfGenerator.round" to "Runde", + "pdfGenerator.seasonLabel" to "赛季:", + "pdfGenerator.sets" to "Sätze", + "pdfGenerator.sportShorts" to "Sportshorts (oder Sportröckchen), am besten auch nicht weiß", + "pdfGenerator.startTime" to "Startzeit", + "pdfGenerator.status" to "Status", + "pdfGenerator.teamAgeGroupLabel" to "球队年龄组:", + "pdfGenerator.teamGenderLabel" to "球队性别:", + "pdfGenerator.teamLineupTitle" to "球队阵容", + "pdfGenerator.teamNameLabel" to "球队:", + "pdfGenerator.time" to "Uhrzeit:", + "pdfGenerator.timeBlock" to "Zeitblock", + "pdfGenerator.tournament" to "Turnier", + "pdfGenerator.trainersPresent" to "Die Trainer probieren bei allen Turnieren anwesend zu sein.", + "pdfGenerator.trainingPlan" to "Trainingsplan", + "pdfGenerator.venue" to "Austragungsort", + "pdfGenerator.venues" to "Austragungsorte", + "pdfGenerator.waterBottle" to "Eine Flasche Wasser dabei haben", + "pendingApprovals.actions" to "Aktionen", + "pendingApprovals.approve" to "Genehmigen", + "pendingApprovals.email" to "Email", + "pendingApprovals.errorApproving" to "Fehler beim Genehmigen des Benutzers", + "pendingApprovals.errorLoadingRequests" to "Fehler beim Laden der ausstehenden Anfragen", + "pendingApprovals.errorRejecting" to "Fehler beim Ablehnen des Benutzers", + "pendingApprovals.firstName" to "Vorname", + "pendingApprovals.lastName" to "Nachname", + "pendingApprovals.noPendingRequests" to "Keine ausstehenden Benutzeranfragen.", + "pendingApprovals.noPermission" to "Keine Berechtigung", + "pendingApprovals.noPermissionDetails" to "Nur Administratoren können Mitgliedsanfragen bearbeiten.", + "pendingApprovals.noPermissionMessage" to "Sie haben keine Berechtigung, Freigaben zu verwalten.", + "pendingApprovals.reject" to "Ablehnen", + "pendingApprovals.title" to "Ausstehende Benutzeranfragen", + "permissions.actions" to "Aktionen", + "permissions.active" to "Aktiv", + "permissions.availableRoles" to "Verfügbare Rollen", + "permissions.baseRole" to "Basis-Rolle", + "permissions.cancel" to "Abbrechen", + "permissions.clickToActivate" to "Klicken zum Aktivieren", + "permissions.clickToDeactivate" to "Klicken zum Deaktivieren", + "permissions.clubMembers" to "Clubmitglieder", + "permissions.creator" to "Ersteller", + "permissions.customize" to "Anpassen", + "permissions.customizeInfo" to "Hier können Sie individuelle Anpassungen vornehmen.", + "permissions.email" to "Email", + "permissions.errorLoadingData" to "Fehler beim Laden der Daten", + "permissions.errorSavingPermissions" to "Fehler beim Speichern der Berechtigungen", + "permissions.errorUpdatingRole" to "Fehler beim Aktualisieren der Rolle", + "permissions.inactive" to "Deaktiviert", + "permissions.loadingMembers" to "Lade Mitglieder...", + "permissions.permissionsFor" to "Berechtigungen für", + "permissions.reset" to "Zurücksetzen", + "permissions.resetAll" to "Alle zurücksetzen", + "permissions.role" to "Rolle", + "permissions.save" to "Speichern", + "permissions.status" to "Status", + "permissions.subtitle" to "Verwalten Sie die Zugriffsrechte für Clubmitglieder", + "permissions.title" to "Berechtigungsverwaltung", + "predefinedActivities.addImage" to "Bild hinzufügen", + "predefinedActivities.cancel" to "Abbrechen", + "predefinedActivities.code" to "Kürzel", + "predefinedActivities.createDrawing" to "Übungszeichnung erstellen", + "predefinedActivities.deduplicate" to "Doppelungen zusammenführen", + "predefinedActivities.delete" to "Löschen", + "predefinedActivities.description" to "Beschreibung", + "predefinedActivities.duration" to "Dauer (Minuten)", + "predefinedActivities.durationText" to "Dauer (Text)", + "predefinedActivities.durationTextPlaceholder" to "z.B. 2x7", + "predefinedActivities.editActivity" to "Aktivität bearbeiten", + "predefinedActivities.editDrawing" to "Übungszeichnung bearbeiten", + "predefinedActivities.excludeFromStats" to "Nicht in Statistik berücksichtigen", + "predefinedActivities.filterAll" to "Alle", + "predefinedActivities.filterCustom" to "Selbstdefiniert", + "predefinedActivities.filterStandard" to "Standard-Aktivitäten", + "predefinedActivities.imageHelp" to "Du kannst entweder einen Link zu einem Bild eingeben oder ein Bild hochladen:", + "predefinedActivities.imageLink" to "Bild-Link (optional)", + "predefinedActivities.imageLinkPlaceholder" to "z.B. https://example.com/bild.jpg oder /api/predefined-activities/:id/image/:imageId", + "predefinedActivities.merge" to "Zusammenführen", + "predefinedActivities.min" to "min", + "predefinedActivities.name" to "Name", + "predefinedActivities.new" to "Neu", + "predefinedActivities.newActivity" to "Neue Aktivität", + "predefinedActivities.orUploadImage" to "Oder Bild hochladen:", + "predefinedActivities.reload" to "Neu laden", + "predefinedActivities.searchPlaceholder" to "Kürzel suchen (z.B. 'as vh us' oder 'vh as us')...", + "predefinedActivities.selectSource" to "Quelle wählen…", + "predefinedActivities.selectTarget" to "Ziel wählen…", + "predefinedActivities.title" to "Vordefinierte Aktivitäten", + "predefinedActivities.upload" to "Hochladen", + "predefinedActivities.uploadAfterSave" to "Nach Speichern hochladen", + "predefinedActivities.uploadedImages" to "Hochgeladene Bilder:", + "predefinedActivities.uploadNote" to "Hinweis: Das Bild wird erst nach dem Speichern der Aktivität hochgeladen.", + "privacy.clubData" to "Vereins-/Aktivitätsdaten", + "privacy.consent" to "Einwilligungsbasierte Vorgänge", + "privacy.cookies" to "Cookies/Local Storage", + "privacy.dataCategories" to "Kategorien personenbezogener Daten", + "privacy.logData" to "Logdaten", + "privacy.memberData" to "Mitgliederdaten", + "privacy.myTischtennisData" to "MyTischtennis-Daten", + "privacy.purposes" to "Zwecke und Rechtsgrundlagen der Verarbeitung", + "privacy.recipients" to "Empfänger", + "privacy.registrationData" to "Registrierungs-/Profildaten", + "privacy.responsible" to "Verantwortlicher", + "privacy.title" to "Datenschutzerklärung", + "privacy.tournamentData" to "Turnierdaten", + "privacy.trainingData" to "Trainingsdaten", + "privacy.usage" to "Nutzung des TrainingsTagebuchs", + "privacy.usageData" to "Nutzungsdaten", + "privacy.website" to "Bereitstellung der Website", + "quickAddMember.birthDate" to "Geburtsdatum (optional)", + "quickAddMember.cancel" to "Abbrechen", + "quickAddMember.createAndAdd" to "Erstellen & Hinzufügen", + "quickAddMember.firstName" to "Vorname", + "quickAddMember.gender" to "Geschlecht", + "quickAddMember.lastName" to "Nachname (optional)", + "quickAddMember.pleaseSelect" to "Bitte wählen", + "quickAddMember.title" to "Neues Mitglied hinzufügen", + "schedule.activeSelection" to "Aktive Auswahl", + "schedule.addressLabel" to "Adresse", + "schedule.adultSchedule" to "Spielplan Erwachsene", + "schedule.ageClass" to "Altersklasse", + "schedule.allLeagueMatches" to "Alle Spiele", + "schedule.balls" to "Bälle", + "schedule.cancel" to "Abbrechen", + "schedule.cityLabel" to "PLZ / Ort", + "schedule.code" to "Code", + "schedule.completedShort" to "Abgeschlossen", + "schedule.copyCode" to "Code kopieren", + "schedule.copyGuestPin" to "Gast-PIN kopieren", + "schedule.copyHomePin" to "Heim-PIN kopieren", + "schedule.date" to "Datum", + "schedule.downloadPDF" to "Download PDF", + "schedule.errorCopying" to "Fehler beim Kopieren", + "schedule.errorImportingCSV" to "Fehler beim Importieren der CSV-Datei", + "schedule.errorLoadingAdultSchedule" to "Fehler beim Laden des Erwachsenenspielplans", + "schedule.errorLoadingGallery" to "Galerie konnte nicht geladen werden.", + "schedule.errorLoadingMatches" to "Fehler beim Laden der Matches", + "schedule.errorLoadingMembers" to "Laden der Mitgliederliste fehlgeschlagen.", + "schedule.errorLoadingOverallSchedule" to "Fehler beim Laden des Gesamtspielplans", + "schedule.errorLoadingTable" to "Fehler beim Laden der Tabellendaten von MyTischtennis", + "schedule.errorLoadingTeams" to "Fehler beim Laden der Teams", + "schedule.errorSavingPlayerSelection" to "Fehler beim Speichern der Spielerauswahl", + "schedule.fetchDataFailed" to "Teamdaten konnten nicht abgerufen werden.", + "schedule.fetchingTeamData" to "Abruf läuft…", + "schedule.fetchStartFailed" to "Abruf konnte nicht gestartet werden.", + "schedule.fetchTeamData" to "Spielplan & Ergebnisse abrufen", + "schedule.fetchTimedOut" to "Zeitüberschreitung beim Abruf.", + "schedule.gallery" to "Mitglieder-Galerie", + "schedule.galleryLoading" to "Galerie wird geladen…", + "schedule.galleryTitle" to "Mitglieder-Galerie - Klicken Sie auf ein Bild, um als 'Bereit' zu markieren", + "schedule.gamesFor" to "Spiele für", + "schedule.guestPin" to "Gast-PIN", + "schedule.guestTeam" to "Gastmannschaft", + "schedule.homePin" to "Heim-PIN", + "schedule.homeTeam" to "Heimmannschaft", + "schedule.imageSize" to "Bildgröße", + "schedule.importSchedule" to "Spielplanimport", + "schedule.leagueTable" to "Ligatabelle", + "schedule.loadingMembers" to "Lade Mitglieder...", + "schedule.locationDialogTitle" to "Spiellokal", + "schedule.locationInfo" to "Ort", + "schedule.locationName" to "Spiellokal", + "schedule.matches" to "Matches", + "schedule.matchLabel" to "Spiel", + "schedule.matchOverviewDescription" to "Steuert, welche Spiele aus der aktuell gewählten Liga im Spielplan angezeigt werden.", + "schedule.matchOverviewTitle" to "Spielübersicht", + "schedule.nextMatch" to "Nächstes Spiel", + "schedule.noActiveMembers" to "Keine aktiven Mitglieder gefunden", + "schedule.noGames" to "Keine Spiele vorhanden", + "schedule.noLeagueForTeam" to "Für dieses Team ist keine Liga hinterlegt. Bitte zuerst eine Liga zuordnen.", + "schedule.noMatchesForPDF" to "Keine Matches gefunden, um PDF zu generieren.", + "schedule.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche.", + "schedule.noMembersWithImages" to "Keine Mitglieder mit Bildern gefunden.", + "schedule.noSelectionMessage" to "Wählen Sie links einen Gesamtspielplan, den Erwachsenenspielplan oder ein Team aus.", + "schedule.noSelectionTitle" to "Noch keine Auswahl aktiv", + "schedule.noTableData" to "Keine Tabellendaten verfügbar", + "schedule.noTeamsFound" to "Keine Teams für diese Saison gefunden", + "schedule.openMatchReport" to "Spielberichtsbogen öffnen", + "schedule.otherTeamMatches" to "Andere Mannschaft", + "schedule.overallSchedule" to "Gesamtspielplan", + "schedule.ownTeamMatches" to "Eigene Mannschaft", + "schedule.pendingShort" to "Offen", + "schedule.planned" to "Vorgesehen", + "schedule.played" to "Gespielt", + "schedule.player" to "Spieler", + "schedule.playerSelection" to "Spielerauswahl", + "schedule.playerSelectionSaved" to "Spielerauswahl gespeichert", + "schedule.pleaseSelectGame" to "Bitte wählen Sie zuerst ein Spiel aus", + "schedule.points" to "Pkt.", + "schedule.position" to "Platz", + "schedule.ready" to "Bereit", + "schedule.refreshTable" to "Tabelle aktualisieren", + "schedule.result" to "Ergebnis", + "schedule.save" to "Speichern", + "schedule.scheduleImportSuccess" to "Spielplan erfolgreich importiert!", + "schedule.schedulePDF" to "Spielpläne.pdf", + "schedule.scheduleTab" to "Spielplan", + "schedule.searchTeams" to "Team oder Liga suchen", + "schedule.selection" to "Auswahl", + "schedule.selectOtherTeam" to "Andere Mannschaft wählen", + "schedule.selectSpecificLeague" to "Bitte wählen Sie eine spezifische Liga aus, um die Tabelle zu laden.", + "schedule.sets" to "Sätze", + "schedule.showLocation" to "Spiellokal anzeigen", + "schedule.subtitle" to "Teams auswählen, Spieltage prüfen und Ligatabellen im Blick behalten.", + "schedule.tableDataLoaded" to "Tabellendaten erfolgreich von MyTischtennis geladen!", + "schedule.tableLoading" to "Tabelle wird geladen…", + "schedule.tableTab" to "Tabelle", + "schedule.team" to "Team", + "schedule.teamDataFetched" to "Teamdaten erfolgreich aktualisiert.", + "schedule.teamDataFetchedDetails" to "Mannschaft: {team}\nVerarbeitete Datensätze: {count}", + "schedule.teams" to "Teams", + "schedule.time" to "Uhrzeit", + "schedule.title" to "Spielpläne", + "schedule.vs" to "vs", + "schedule.workspaceScheduleDescription" to "{matches} Spiele, davon {completed} abgeschlossen und {pending} offen.", + "schedule.workspaceTableDescription" to "{count} Tabellenzeilen aktuell geladen.", + "seasonSelector.addSeason" to "Neue Saison hinzufügen", + "seasonSelector.alreadyExists" to "Diese Saison existiert bereits!", + "seasonSelector.cancel" to "Abbrechen", + "seasonSelector.create" to "Erstellen", + "seasonSelector.errorCreating" to "Fehler beim Erstellen der Saison", + "seasonSelector.errorLoading" to "Fehler beim Laden der Saisons", + "seasonSelector.hint" to "Hinweis", + "seasonSelector.label" to "Saison:", + "seasonSelector.loading" to "Lade...", + "seasonSelector.newSeason" to "Neue Saison:", + "seasonSelector.placeholder" to "z.B. 2023/2024", + "seasonSelector.selectSeason" to "Saison wählen...", + "settings.language" to "语言", + "settings.languageChanged" to "语言已成功更改", + "settings.languageDescription" to "选择您偏好的应用程序语言", + "settings.personalSettings" to "个人设置", + "settings.selectLanguage" to "选择语言", + "settings.title" to "设置", + "tagHistory.noHistory" to "Keine Tag-Historie vorhanden", + "tagHistory.selectTags" to "Tags auswählen", + "tagHistory.title" to "Tag-Historie", + "teamManagement.activeTeam" to "Aktives Team", + "teamManagement.addPlanningTeam" to "Planungs-Team hinzufügen", + "teamManagement.allMembersAssigned" to "Alle Mitglieder sind aktuell zugeordnet.", + "teamManagement.assignLeagueBeforeDocuments" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, damit Dokumente verarbeitet werden können.", + "teamManagement.assignLeagueBeforeParsing" to "Bitte ordnen Sie dem Team zuerst eine Liga zu, um PDF-Dateien zu parsen.", + "teamManagement.assignMemberToTeam" to "Zu Team hinzufügen", + "teamManagement.association" to "Verband", + "teamManagement.asyncJobStartFailed" to "Async-Job konnte nicht gestartet werden.", + "teamManagement.autoFetchEnabled" to "Automatischer Datenabruf ist aktiviert", + "teamManagement.automaticJobs" to "Automatische Jobs", + "teamManagement.autoSaved" to "Automatisch gespeichert", + "teamManagement.autoSaveError" to "Automatisches Speichern fehlgeschlagen", + "teamManagement.availableLineupMembers" to "可用球员", + "teamManagement.basicSettings" to "Grundeinstellungen", + "teamManagement.basicSettingsIntro" to "Teamname und Liga in einem ruhigen Formular prüfen und anpassen.", + "teamManagement.change" to "Ändern", + "teamManagement.clearFields" to "Felder leeren", + "teamManagement.codeList" to "Code-Liste", + "teamManagement.configurationStatus" to "Konfigurationsstatus", + "teamManagement.configureLeagueDetails" to "Verband: {association}\nSaison: {season}\nLiga: {league}\nGruppen-ID: {groupId}\n\nMöchten Sie diese Liga in der Datenbank konfigurieren? Dies ermöglicht es, Tabellendaten automatisch abzurufen.", + "teamManagement.configureLeagueTitle" to "Liga konfigurieren?", + "teamManagement.createAndEdit" to "Anlegen & Bearbeiten", + "teamManagement.created" to "Erstellt", + "teamManagement.createNewTeam" to "Neues Team anlegen", + "teamManagement.dataFetchFailed" to "Daten konnten nicht abgerufen werden.", + "teamManagement.delete" to "Löschen", + "teamManagement.deleteTeamConfirm" to "Möchten Sie das Club-Team \"{name}\" wirklich löschen?", + "teamManagement.deleteTeamTitle" to "Club-Team löschen", + "teamManagement.documentAvailable" to "Vorhanden", + "teamManagement.documentMissing" to "Fehlt", + "teamManagement.documentNotFound" to "Das ausgewählte Dokument wurde nicht gefunden.", + "teamManagement.documentParsedSummary" to "{label} erfolgreich hochgeladen und geparst!\n\nGefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.documents" to "Dokumente", + "teamManagement.documentsIntro" to "Code- und PIN-Listen hochladen, prüfen und bei Bedarf erneut parsen.", + "teamManagement.documentUploaded" to "{label} \"{fileName}\" wurde erfolgreich hochgeladen!", + "teamManagement.edit" to "Bearbeiten", + "teamManagement.editTeam" to "Team bearbeiten", + "teamManagement.eligibility" to "参赛资格", + "teamManagement.eligibilityAdultRelease" to "允许参加成人组", + "teamManagement.eligibilityAdultReleaseAndReserve" to "允许参加成人组 + 替补", + "teamManagement.eligibilityAdultReserve" to "成人组替补", + "teamManagement.eligibilityRegular" to "常规", + "teamManagement.enterUrlForAutoConfig" to "MyTischtennis-URL eingeben für automatische Konfiguration", + "teamManagement.error" to "Fehler", + "teamManagement.errorConfiguringLeague" to "Liga konnte nicht konfiguriert werden.", + "teamManagement.errorConfiguringTeam" to "Team konnte nicht konfiguriert werden.", + "teamManagement.errorDeletingTeam" to "Fehler beim Löschen des Club-Teams.", + "teamManagement.errorLoadingPdf" to "Fehler beim Laden des PDFs.", + "teamManagement.errorLoadingStats" to "Statistiken konnten nicht geladen werden.", + "teamManagement.errorParsingPdf" to "Fehler beim Parsen der PDF-Datei", + "teamManagement.errorParsingUrl" to "URL konnte nicht geparst werden. Bitte überprüfen Sie das Format.", + "teamManagement.errorsCount" to "Fehler: {count}", + "teamManagement.errorUploadingDocument" to "Fehler beim Hochladen und Parsen der Datei.", + "teamManagement.exportLineupPdf" to "导出阵容为 PDF", + "teamManagement.fetched" to "abgerufen", + "teamManagement.fetchTimedOut" to "Zeitüberschreitung beim Abruf (Async-Job läuft zu lange).", + "teamManagement.fetchTimeoutShort" to "Zeitüberschreitung beim Abruf (Timeout).", + "teamManagement.fileTooLarge" to "{label} darf maximal 10 MB groß sein.", + "teamManagement.fileTooLargeTitle" to "Datei zu groß", + "teamManagement.filterAll" to "Alle", + "teamManagement.filterConfigured" to "Konfiguriert", + "teamManagement.filterNeedsAttention" to "Prüfen", + "teamManagement.filterNoLeague" to "Ohne Liga", + "teamManagement.firstHalf" to "上半程", + "teamManagement.firstHalfFull" to "上半程(7月 - 12月)", + "teamManagement.fullyConfigured" to "Vollständig konfiguriert", + "teamManagement.groupId" to "Gruppen-ID", + "teamManagement.groupName" to "Gruppenname", + "teamManagement.invalidFileExtension" to "{label} muss eine der folgenden Endungen haben: {extensions}.", + "teamManagement.invalidFileTypeTitle" to "Ungültiger Dateityp", + "teamManagement.invalidMimeType" to "{label} weist einen unerwarteten MIME-Typ auf: {type}.", + "teamManagement.lastRun" to "Zuletzt", + "teamManagement.lastUpdated" to "Zuletzt aktualisiert", + "teamManagement.latestUpload" to "Zuletzt hochgeladen: {date}", + "teamManagement.league" to "Spielklasse", + "teamManagement.leagueConfiguredDetails" to "Liga: {league}\nSaison: {season}\nVerband: {association}\nGruppen-ID: {groupId}\n\nTabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueConfiguredSuccess" to "Liga erfolgreich konfiguriert! Tabellendaten können jetzt automatisch abgerufen werden.", + "teamManagement.leagueFieldRequired" to "请选择联赛级别并保存。", + "teamManagement.lineupEmpty" to "这支队伍还没有报名任何球员。", + "teamManagement.lineupPdfEmpty" to "阵容中没有球员,无法生成 PDF。", + "teamManagement.lineupPdfFilePrefix" to "阵容", + "teamManagement.lineupProposal" to "按 QTTR 的队伍报名", + "teamManagement.lineupSaved" to "Mannschaftsmeldung gespeichert.", + "teamManagement.lineupSaveError" to "无法保存队伍报名。", + "teamManagement.lineupUnsavedChanges" to "Ungespeicherte Änderungen in der Mannschaftsmeldung.", + "teamManagement.lineupValidationTooLargeGap" to "{higher} 比 {lower} 高出超过 30 个 QTTR 积分。请更正这个顺序。", + "teamManagement.loadingStats" to "Lade Statistiken...", + "teamManagement.manualFetch" to "Abrufen", + "teamManagement.markAsInterested" to "Als interessiert markieren", + "teamManagement.matchesSummary" to "Gefundene Spiele: {matchesFound}\nNeue Spiele erstellt: {created}\nSpiele aktualisiert: {updated}", + "teamManagement.matchResults" to "Spielergebnisse", + "teamManagement.missingConfigSummary" to "缺少的信息:", + "teamManagement.missingItems" to "Fehlend: {items}", + "teamManagement.missingLeagueForTeam" to "Für das ausgewählte Team wurde keine Liga übermittelt.", + "teamManagement.moreErrors" to "... und {count} weitere", + "teamManagement.myTischtennis" to "MyTischtennis", + "teamManagement.myTischtennisIntro" to "URL einfügen, Konfiguration prüfen und bei Bedarf Daten manuell abrufen.", + "teamManagement.mytischtennisLoginRequired" to "Login bei myTischtennis erforderlich", + "teamManagement.myTischtennisUrlPlaceholder" to "MyTischtennis URL...", + "teamManagement.myTischtennisUrlRequired" to "粘贴并解析 MyTischtennis 链接以获取球队 ID 和联赛数据。", + "teamManagement.never" to "Nie", + "teamManagement.newTeam" to "Neues Team", + "teamManagement.noAssignment" to "Keine Zuordnung", + "teamManagement.noAutomaticUpdate" to "Noch keine automatische Aktualisierung", + "teamManagement.noDocumentUploadYet" to "Noch kein Dokument hochgeladen.", + "teamManagement.noIssues" to "Keine offenen Konfigurationsprobleme.", + "teamManagement.noLeague" to "Keine Spielklasse", + "teamManagement.noMatchesFoundDetails" to "Hinweis: Keine Spiele erkannt.\nZeilen im Dokument: {lines}", + "teamManagement.noMatchesFoundTitle" to "Keine Spiele gefunden", + "teamManagement.noMatchingTeams" to "Keine Teams passen zur aktuellen Suche oder Filterung.", + "teamManagement.noMembersInPool" to "Keine passenden Mitglieder gefunden.", + "teamManagement.noPlannedLeague" to "Keine geplante Spielklasse", + "teamManagement.noPlayerStats" to "Keine Spieleinsätze erfasst.", + "teamManagement.notConfigured" to "Nicht konfiguriert", + "teamManagement.notCreated" to "Nicht erstellt", + "teamManagement.noTeamsYet" to "Noch keine Teams vorhanden. Erstellen Sie Ihr erstes Team!", + "teamManagement.openDocument" to "Anzeigen", + "teamManagement.openInWorkspace" to "Zum Bearbeiten öffnen", + "teamManagement.parseDocument" to "Neu parsen", + "teamManagement.parseUrlAction" to "URL prüfen", + "teamManagement.partiallyConfigured" to "Teilweise konfiguriert", + "teamManagement.pdfFileNotFound" to "Die PDF-Datei konnte nicht gefunden werden.", + "teamManagement.pinList" to "Pin-Liste", + "teamManagement.plannedLeague" to "计划联赛", + "teamManagement.plannedLeagueHint" to "选填。与已报名联赛(MyTischtennis)无关。", + "teamManagement.plannedLeaguePlaceholder" to "例如:地区联赛,下次报名后 …", + "teamManagement.planningSubtitle" to "Mitglieder auf geplante Teams verteilen, Reihenfolge festlegen und offene Zuordnungen sehen.", + "teamManagement.planningTitle" to "Mannschaftsplanung", + "teamManagement.player" to "Spieler", + "teamManagement.playersPoolSubtitle" to "Alle aktiven Mitglieder mit Spielinteresse", + "teamManagement.playerStats" to "Spieleinsätze", + "teamManagement.playerStatsIntro" to "Schneller Überblick über Einsätze der Mannschaft in dieser Saison.", + "teamManagement.playersWantToPlay" to "Wollen spielen", + "teamManagement.qttr" to "(Q)TTR-Wert", + "teamManagement.ratingUpdates" to "Rating-Updates", + "teamManagement.refreshStats" to "Aktualisieren", + "teamManagement.reuploadFile" to "Bitte laden Sie die Datei erneut hoch und versuchen Sie es noch einmal.", + "teamManagement.searchMembers" to "Mitglied suchen", + "teamManagement.searchTeams" to "Team suchen", + "teamManagement.season" to "Saison", + "teamManagement.seasonFull" to "Gesamte Saison (ab 1. Juli)", + "teamManagement.seasonUnknown" to "unbekannt", + "teamManagement.secondHalf" to "下半程", + "teamManagement.secondHalfFull" to "下半程(1月1日起)", + "teamManagement.selectedLineup" to "已报名球员", + "teamManagement.selectMember" to "Mitglied auswählen", + "teamManagement.selectTeam" to "Team auswählen", + "teamManagement.selectTeamFirst" to "Bitte wählen Sie zuerst ein Team aus", + "teamManagement.selectTeamForConfiguration" to "Um die MyTischtennis-Konfiguration zu aktivieren, müssen Sie zuerst ein Team aus der Liste auswählen.", + "teamManagement.selectTeamTitle" to "Team auswählen", + "teamManagement.showCodeList" to "Code-Liste anzeigen", + "teamManagement.showPinList" to "Pin-Liste anzeigen", + "teamManagement.sortFirstLast" to "Sortierung: Vorname Nachname", + "teamManagement.sortLastFirst" to "Sortierung: Nachname Vorname", + "teamManagement.status" to "Status", + "teamManagement.subtitle" to "Teams auswählen, Konfiguration prüfen und Liga-Daten an einem Ort pflegen.", + "teamManagement.successful" to "Erfolgreich", + "teamManagement.tableUpdateLabel" to "Tabellenaktualisierung:", + "teamManagement.tableUrlDetected" to "Tabellen-URL erkannt", + "teamManagement.team" to "Team", + "teamManagement.teamAgeGroup" to "Altersklasse", + "teamManagement.teamConfiguredDetails" to "Liga: {league}\nSaison: {season}\nAutomatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamConfiguredSuccess" to "Team erfolgreich konfiguriert! Automatischer Datenabruf ist jetzt aktiv.", + "teamManagement.teamDataFetched" to "Teamdaten erfolgreich abgerufen.", + "teamManagement.teamDataFetchedDetails" to "Team: {team}\nAbgerufene Datensätze: {count}", + "teamManagement.teamGender" to "Geschlecht", + "teamManagement.teamGenderFemale" to "Weiblich", + "teamManagement.teamGenderOpen" to "Offen", + "teamManagement.teamHasNoLeague" to "Dieses Team ist keiner Liga zugeordnet.", + "teamManagement.teamId" to "Team-ID", + "teamManagement.teamName" to "Team-Name", + "teamManagement.teamNamePlaceholder" to "z.B. Herren 1, Damen 2", + "teamManagement.teams" to "Teams", + "teamManagement.title" to "Team-Verwaltung", + "teamManagement.unassignedMembers" to "Noch nicht zugeordnet", + "teamManagement.unassignedMembersSubtitle" to "Mitglieder ohne Team-Zuordnung", + "teamManagement.unknown" to "Unbekannt", + "teamManagement.unknownTeam" to "Unbekanntes Team", + "teamManagement.updated" to "aktualisiert", + "teamManagement.uploadNewVersion" to "Neue Version hochladen", + "tournament.apply" to "Übernehmen", + "tournaments.action" to "Aktion", + "tournaments.add" to "Hinzufügen", + "tournaments.addClass" to "Klasse hinzufügen", + "tournaments.addClubMember" to "Vereinsmitglied hinzufügen", + "tournaments.addExternalParticipant" to "Externen Teilnehmer hinzufügen", + "tournaments.addPairing" to "Paarung hinzufügen", + "tournaments.addPoolRule" to "添加分组规则", + "tournaments.address" to "地址", + "tournaments.adjustTournamentSearch" to "Passe den Suchbegriff an oder lege ein neues Turnier an.", + "tournaments.advancersPerGroup" to "Aufsteiger pro Gruppe", + "tournaments.allClasses" to "Alle Klassen", + "tournaments.allDataComplete" to "所有参赛者数据已完整。", + "tournaments.allDataCompleteTop3" to "前3名的所有数据已完整。", + "tournaments.apply" to "Übernehmen", + "tournaments.assignmentReviewTitle" to "{count} participants need a choice:", + "tournaments.atLeastOnePoolRule" to "请至少为 {label} 添加一条分组规则(例如名次 1,2)。", + "tournaments.autoAssignableParticipantsHint" to "{count} of them can be assigned automatically.", + "tournaments.autoAssignEligible" to "Assign automatically", + "tournaments.autoAssignEligibleDone" to "{count} participants were assigned automatically.", + "tournaments.autoAssignEligibleNone" to "There are currently no participants with a single automatic class assignment.", + "tournaments.autoAssignEligiblePartial" to "{successCount} participants were assigned automatically, {errorCount} failed.", + "tournaments.birthdate" to "Geburtsdatum", + "tournaments.cancel" to "Abbrechen", + "tournaments.class" to "Klasse", + "tournaments.classes" to "Klassen", + "tournaments.className" to "Klassenname", + "tournaments.cleanupOrphanedMatches" to "Verwaiste Spiele aufräumen", + "tournaments.club" to "Verein", + "tournaments.clubMember" to "(Vereinsmitglied)", + "tournaments.conflictSuggestionLabel" to "Suggestions:", + "tournaments.correctMatch" to "更正", + "tournaments.create" to "Erstellen", + "tournaments.createFinalFromIntermediate" to "从中间轮创建决赛轮", + "tournaments.createFinalFromPreliminary" to "从预赛创建决赛轮", + "tournaments.createGroupMatches" to "Gruppenspiele berechnen", + "tournaments.createGroups" to "Gruppen erstellen", + "tournaments.createIntermediateFromPreliminary" to "从预赛创建中间轮", + "tournaments.createMatches" to "Spiele erstellen", + "tournaments.createSuggestedPairings" to "Create pairings", + "tournaments.currentClass" to "Aktive Klasse", + "tournaments.dataNotRecorded" to "尚未录入", + "tournaments.date" to "Datum", + "tournaments.delete" to "Löschen", + "tournaments.deleteClassConfirm" to "确定要删除组别“{name}”吗?", + "tournaments.deleteClassParticipantsDetached" to "所有参赛者将从该组别中移除。", + "tournaments.deleteClassTitle" to "删除组别", + "tournaments.deleteExistingPairingsConfirm" to "现有配对将被删除。是否继续?", + "tournaments.deleteKORound" to "K.o.-Runde", + "tournaments.diff" to "Diff", + "tournaments.distributeTables" to "分配空闲球台", + "tournaments.distributeTablesResult" to "球台分配", + "tournaments.doubles" to "Doppel", + "tournaments.doublesPairingHint" to "{count} participants in this doubles class still need a partner.", + "tournaments.doublesTournament" to "Doppel-Turnier", + "tournaments.doublesTournamentHint" to "Schaltet alle vorhandenen Klassen auf Doppel und legt neue Klassen standardmäßig als Doppel an.", + "tournaments.edit" to "Bearbeiten", + "tournaments.eligibleClassesHint" to "适合: {classes}", + "tournaments.email" to "E-Mail", + "tournaments.encounter" to "Begegnung", + "tournaments.enterClassName" to "请输入组别名称。", + "tournaments.enterExternalParticipantName" to "请至少输入名字和姓氏。", + "tournaments.enterMiniLocation" to "请输入地点。", + "tournaments.errorCreatingGroups" to "Fehler beim Erstellen der Gruppen.", + "tournaments.errorCreatingTournament" to "Fehler beim Erstellen des Turniers.", + "tournaments.errorDistributingTables" to "分配球台时出错。", + "tournaments.errorGeneratingPdf" to "生成 PDF 时出错。", + "tournaments.errorMergingClasses" to "Fehler beim Zusammenlegen der Klassen.", + "tournaments.errorMoreSeededThanUnseeded" to "Es gibt mehr gesetzte als nicht gesetzte Spieler. Zufällige Paarungen können nicht erstellt werden.", + "tournaments.errorResettingKORound" to "Fehler beim Zurücksetzen der K.o.-Runde.", + "tournaments.errorUpdatingTournament" to "Fehler beim Aktualisieren des Turniers.", + "tournaments.exportPDF" to "PDF exportieren", + "tournaments.external" to "Extern", + "tournaments.finalPlacements" to "Endplatzierungen", + "tournaments.finalRound" to "决赛轮", + "tournaments.finishMatch" to "结束比赛", + "tournaments.firstName" to "Vorname", + "tournaments.forForwarding" to "für Weitermeldung", + "tournaments.gaveUp" to "Aufgegeben", + "tournaments.gaveUpHint" to "Spieler hat aufgegeben – alle Spiele zählen für den Gegner (11:0) bzw. 0:0 bei beiden aufgegeben.", + "tournaments.genderAll" to "Alle", + "tournaments.genderMixed" to "Mixed", + "tournaments.generatingPDF" to "正在生成PDF...", + "tournaments.group" to "Gruppe", + "tournaments.groupMatches" to "Gruppenspiele", + "tournaments.groupNumber" to "Gruppe", + "tournaments.groupPlacements" to "Gruppenplatzierungen", + "tournaments.groupsLabel" to "分组", + "tournaments.groupsOverview" to "Gruppenübersicht", + "tournaments.groupsPerClass" to "Gruppen", + "tournaments.groupsPerClassHint" to "Geben Sie für jede Klasse die Anzahl der Gruppen ein (0 = keine Gruppen für diese Klasse):", + "tournaments.index" to "Index", + "tournaments.intermediateRound" to "中间轮(第2轮)", + "tournaments.internalStatsAbsoluteRank" to "总分排名", + "tournaments.internalStatsAgeFilter" to "年龄与性别(单打)", + "tournaments.internalStatsAgeFilterAll" to "全部年龄组", + "tournaments.internalStatsAgeFilterNone" to "未选择年龄组", + "tournaments.internalStatsAgeNoClass" to "未分配级别", + "tournaments.internalStatsAgeSelectAll" to "全选", + "tournaments.internalStatsAgeSelectNone" to "全不选", + "tournaments.internalStatsAverageRank" to "平均排名(每场锦标赛)", + "tournaments.internalStatsAvgPoints" to "平均", + "tournaments.internalStatsEmpty" to "所选期间没有可统计的数据。", + "tournaments.internalStatsExportPdf" to "导出 PDF", + "tournaments.internalStatsFilterAgeBands" to "Altersklassen", + "tournaments.internalStatsFilterGenderColumn" to "Geschlecht", + "tournaments.internalStatsGenderScopeAll" to "Alle", + "tournaments.internalStatsGenderScopeGirls" to "Mädchen", + "tournaments.internalStatsLast12Months" to "过去 12 个月", + "tournaments.internalStatsLast3Months" to "过去 3 个月", + "tournaments.internalStatsLast6Months" to "过去 6 个月", + "tournaments.internalStatsOpenButton" to "打开单打统计数据", + "tournaments.internalStatsPeriod" to "时间范围", + "tournaments.internalStatsPoints" to "合计", + "tournaments.internalStatsPointsExplain" to "计分:每组用百分比表示名次(有 N 人排名:第 1 名 100%,最后一名 0%,中间线性;同名次同分)。N 为该组所有有排名者(含嘉宾)。仅 1 人时记 100%。进入淘汰赛者获得该级别最高小组值加 1,之后每赢一场淘汰赛再加 1。仅统计俱乐部成员单打。", + "tournaments.internalStatsTitle" to "内部锦标赛统计(单打)", + "tournaments.internalStatsTournamentsInPeriod" to "该期间共 {count} 场锦标赛(不含迷你锦标赛)。", + "tournaments.internalStatsTtAdult" to "成人组", + "tournaments.internalTournaments" to "Interne Turniere", + "tournaments.knockoutLabel" to "KO", + "tournaments.koRound" to "K.-o.-Runde", + "tournaments.lastName" to "Nachname", + "tournaments.livePosition" to "Live-Platz", + "tournaments.loadFromTraining" to "Aus Trainingstag laden", + "tournaments.markMatchLive" to "标记为进行中", + "tournaments.maxGroupSize" to "Maximale Gruppengröße", + "tournaments.mergeClasses" to "Klassen zusammenlegen (gemeinsame Gruppenphase)", + "tournaments.mergeDistribute" to "Spieler aus A auf alle Gruppen (von B) verteilen", + "tournaments.mergeSingleGroup" to "Alle Spieler aus A in eine Gruppe (bei B)", + "tournaments.minBirthYear" to "Geboren im Jahr oder später", + "tournaments.miniChampionshipLocation" to "Ort", + "tournaments.miniChampionships" to "Minimeisterschaften", + "tournaments.miniChampionshipYear" to "Jahr", + "tournaments.miniChampionshipYearHint" to "12 = in diesem Jahr 12 oder 13 Jahre, 10 = 10 oder 11, 8 = 9 und jünger", + "tournaments.minimumParticipantsForPairings" to "配对至少需要 2 名参赛者。", + "tournaments.missingDataPDF" to "缺失数据导出为PDF", + "tournaments.missingDataPDFSubtitle" to "请向参赛者收集缺失数据(标记为____)并在此记录。", + "tournaments.missingDataPDFSubtitleTop3" to "请收集前3名的缺失数据(标记为____)并在此记录。", + "tournaments.missingDataPDFTitle" to "参赛者缺失数据 – 迷你锦标赛", + "tournaments.missingDataPDFTitleTop3" to "缺失数据 – 前3名迷你锦标赛", + "tournaments.name" to "Name", + "tournaments.newMiniChampionship" to "Neue Minimeisterschaft", + "tournaments.newSetPlaceholder" to "新一局,例如 11:7", + "tournaments.newTournament" to "Neues Turnier", + "tournaments.noAssignableMatches" to "没有两位选手都空闲的比赛。", + "tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.", + "tournaments.noEligibleClass" to "未找到合适的组别。", + "tournaments.noFreeTables" to "没有可用的空闲球台。", + "tournaments.noMatchingTournamentsTitle" to "Keine passenden Turniere", + "tournaments.noOrphanedMatchesFound" to "未找到孤立比赛。", + "tournaments.noPlacementsYet" to "Noch keine Platzierungen vorhanden.", + "tournaments.noPlayerDataAvailable" to "Keine Spielerdaten verfügbar", + "tournaments.noPoolRulesYet12" to "还没有规则。示例:第1和第2名 -> 第2轮上层小组。", + "tournaments.noPoolRulesYetFinal" to "还没有规则。示例:第1和第2名 -> 决赛轮。", + "tournaments.noTop3Yet" to "前3名尚未确定。", + "tournaments.noTournamentDate" to "没有赛事日期。", + "tournaments.noTournamentsCreatedYet" to "Es wurden noch keine Turniere angelegt.", + "tournaments.noTrainingOnDate" to "未找到 {date} 的训练。", + "tournaments.noTrainingParticipants" to "在该日期的训练中未找到参赛者。", + "tournaments.noValidTrainingParticipants" to "在该日期的训练中未找到有效参赛者。", + "tournaments.numberOfGroups" to "Anzahl Gruppen", + "tournaments.numberOfTables" to "球台数量", + "tournaments.openTournaments" to "Offene Turniere", + "tournaments.optional" to "optional", + "tournaments.orphanedMatchesRemoved" to "已移除 {count} 场孤立比赛。", + "tournaments.outOfCompetition" to "Spieler aus A außer Konkurrenz", + "tournaments.page" to "页", + "tournaments.pairingAlreadyExists" to "该配对已存在。", + "tournaments.pairings" to "Doppel-Paarungen", + "tournaments.pairingsCreatedWithErrors" to "已创建 {successCount} 个配对,错误 {errorCount} 个。", + "tournaments.participantConflicts" to "冲突", + "tournaments.participantNotFound" to "未找到参赛者。", + "tournaments.participants" to "Teilnehmer", + "tournaments.participantsNeedClassAssignment" to "{count} Teilnehmer sind noch keiner Klasse zugeordnet. Diese sollten zuerst zugeordnet werden.", + "tournaments.phone" to "电话", + "tournaments.placementsPendingFinals" to "Endplatzierungen erscheinen, sobald mehrere Gruppen oder eine Endrunde vorhanden sind.", + "tournaments.placementsPendingGroups" to "Gruppenplatzierungen erscheinen, sobald Gruppenspiele vorhanden sind.", + "tournaments.placementsPendingNoGroups" to "Sobald Gruppenspiele oder eine Endrunde vorhanden sind, erscheinen hier die Platzierungen.", + "tournaments.placementsPendingSelectedClass" to "Für die aktuell gewählte Klasse gibt es noch keine Platzierungen.", + "tournaments.placementsPendingTitle" to "Platzierungen noch nicht verfügbar", + "tournaments.placesFromEachGroup" to "每组名次(例如 1,2)", + "tournaments.player" to "Spieler", + "tournaments.playerOne" to "选手 1", + "tournaments.playerTwo" to "选手 2", + "tournaments.playInGroups" to "Spielen in Gruppen", + "tournaments.playThirdPlace" to "进行三四名决赛", + "tournaments.pleaseEnterDate" to "Bitte geben Sie ein Datum ein!", + "tournaments.pleaseSelectParticipant" to "Bitte wählen Sie einen Teilnehmer aus!", + "tournaments.points" to "Punkte", + "tournaments.pointsRatio" to "Spielpunkte", + "tournaments.poolRuleLabel" to "Rule {number}", + "tournaments.poolRulePlacesHelp" to "Example: 1 or 1,2", + "tournaments.poolRulePlacesLabel" to "Which places advance?", + "tournaments.poolRulePreview" to "From each group, places {places} advance to {target}.", + "tournaments.poolRuleQuickExamples" to "Quick examples:", + "tournaments.poolRuleSummary" to "Places {places} go to {target}", + "tournaments.poolRuleTargetGroupsDetailed" to "{count} target groups", + "tournaments.poolRuleTargetKnockout" to "the knockout bracket", + "tournaments.poolRuleTargetLabel" to "Where do these places go?", + "tournaments.position" to "Platz", + "tournaments.problemConfigDescription" to "Check date, name and winning sets.", + "tournaments.problemConfigTitle" to "Configuration incomplete", + "tournaments.problemConflictsDescription" to "Review invalid classes, missing data or unclear assignments.", + "tournaments.problemConflictsTitle" to "{count} participants with conflicts", + "tournaments.problemDoublesAutoDescription" to "The open doubles class can be paired automatically right away.", + "tournaments.problemDoublesDescription" to "One or more doubles classes still need partner assignments.", + "tournaments.problemDoublesTitle" to "{count} open doubles partners", + "tournaments.problemGroupMatchesDescription" to "The groups are ready, but the matches have not been created yet.", + "tournaments.problemGroupMatchesTitle" to "Group matches not generated yet", + "tournaments.problemGroupsMissingDescription" to "The group tournament needs groups to be created first.", + "tournaments.problemGroupsMissingTitle" to "Groups not created yet", + "tournaments.problemKnockoutReadyDescription" to "The group stage is complete and the final round can be generated now.", + "tournaments.problemKnockoutReadyTitle" to "Knockout can be started", + "tournaments.problemUnassignedAutoDescription" to "{count} of them can be assigned automatically right away.", + "tournaments.problemUnassignedDescription" to "These participants still need a manual class assignment.", + "tournaments.problemUnassignedTitle" to "{count} participants without class", + "tournaments.promotionRule12" to "晋级:预赛 → 中间轮 (1→2)", + "tournaments.promotionRuleDescription12" to "Define which places from each preliminary group advance to the intermediate round.", + "tournaments.promotionRuleDescriptionFinalGroups" to "Define which places from the previous round advance to the final-round groups.", + "tournaments.promotionRuleDescriptionFinalKnockout" to "Define which places from the previous round advance to the knockout bracket.", + "tournaments.promotionRuleFinal" to "晋级:第 {from} 轮 → 第 {to} 轮", + "tournaments.randomizeGroups" to "Zufällig verteilen", + "tournaments.randomPairings" to "Zufällige Doppel-Paarungen", + "tournaments.randomPairingsCreated" to "Zufällige Paarungen wurden erstellt.", + "tournaments.resetGroupMatches" to "Gruppenspiele", + "tournaments.resetGroups" to "Gruppen zurücksetzen", + "tournaments.result" to "Ergebnis", + "tournaments.resultsRanking" to "排名", + "tournaments.round" to "Runde", + "tournaments.roundGroupCount" to "小组数量", + "tournaments.roundMode" to "模式", + "tournaments.ruleStatusBlocked" to "Blocked", + "tournaments.ruleStatusOk" to "Looks good", + "tournaments.ruleStatusOkDescription" to "This rule matches the current target round and can be used as-is.", + "tournaments.ruleStatusReview" to "Review", + "tournaments.save" to "Speichern", + "tournaments.saveRounds" to "保存轮次", + "tournaments.seeded" to "Gesetzt", + "tournaments.selectClass" to "Klasse auswählen", + "tournaments.selectClassPrompt" to "Bitte wählen Sie oben eine Klasse aus.", + "tournaments.selectedTournament" to "Aktives Turnier", + "tournaments.selectParticipant" to "-- Teilnehmer auswählen --", + "tournaments.selectPlayer" to "Spieler auswählen", + "tournaments.selectTournamentFirst" to "请先选择一个赛事。", + "tournaments.selectTwoDifferentPlayers" to "请选择两位不同的选手。", + "tournaments.setDiff" to "Satzdifferenz", + "tournaments.sets" to "Sätze", + "tournaments.showClass" to "Klasse anzeigen", + "tournaments.showingTournamentCount" to "{visible} von {total}", + "tournaments.showPlayerDetails" to "Spielerdetails anzeigen", + "tournaments.singles" to "Einzel", + "tournaments.sourceClass" to "Quelle", + "tournaments.stageActionMissingRulesHint" to "Create at least one advancement rule first before moving players into the next round.", + "tournaments.stageActionRun" to "Run now", + "tournaments.stageActionsDescription" to "These steps move qualified players into the next round.", + "tournaments.stageActionsTitle" to "Next actions", + "tournaments.stageConfigBadServerResponse" to "服务器响应无效。", + "tournaments.stageConfigCurrentSetup" to "Current structure", + "tournaments.stageConfigIntro" to "中间轮是可选的。如果启用,之后总会有一个决赛轮。淘汰赛决赛轮会生成为单一对阵表。", + "tournaments.stageConfigLoadError" to "加载轮次配置时出错。", + "tournaments.stageConfigLoading" to "正在加载轮次配置…", + "tournaments.stageConfigMissingIds" to "无法保存:缺少俱乐部或赛事 ID。", + "tournaments.stageConfigTitle" to "中间轮与决赛轮", + "tournaments.stageCreated" to "第 {round} 轮已创建。", + "tournaments.stageFinalDescription" to "Decide whether the final round should be played as groups or directly as a knockout bracket.", + "tournaments.stageFlowBreakdownActionAddRule" to "Add rule", + "tournaments.stageFlowBreakdownActionOpen" to "Open rule", + "tournaments.stageFlowBreakdownActionReview" to "Review issues", + "tournaments.stageFlowBreakdownErrors" to "{count} blocking error(s)", + "tournaments.stageFlowBreakdownMissingRule" to "No complete rule has been added yet", + "tournaments.stageFlowBreakdownOk" to "Configuration looks good", + "tournaments.stageFlowBreakdownWarnings" to "{count} warning(s) open", + "tournaments.stageFlowDirectFinal" to "Preliminary round -> final round ({final})", + "tournaments.stageFlowHealthBlocked" to "Round logic is contradictory", + "tournaments.stageFlowHealthBlockedDescription" to "At least one rule currently does not match the target round or contains blocking configuration errors.", + "tournaments.stageFlowHealthIncomplete" to "Round logic is incomplete", + "tournaments.stageFlowHealthMissingRules" to "At least one transition does not yet have a complete advancement rule.", + "tournaments.stageFlowHealthOk" to "Round logic looks good", + "tournaments.stageFlowHealthOkDescription" to "The transitions between rounds are fully configured and currently coherent.", + "tournaments.stageFlowHealthReviewDescription" to "The round logic is basically usable, but should still be reviewed because of open hints.", + "tournaments.stageFlowPreviewMissing" to "No rule has been configured yet.", + "tournaments.stageFlowPreviewRule" to "Places {places} go to {target}", + "tournaments.stageFlowPreviewRuleWithCount" to "{base} (expected qualifiers: {count})", + "tournaments.stageFlowQualifiedPreviewEntry" to "G{group} · Place {position} · {name}", + "tournaments.stageFlowQualifiedPreviewTitle" to "Currently qualified", + "tournaments.stageFlowReadinessIncompleteGroups" to "{count} group(s) are not fully decided yet", + "tournaments.stageFlowReadinessNoGroups" to "No matching groups exist yet", + "tournaments.stageFlowReadinessReady" to "Transition is ready to start", + "tournaments.stageFlowRecommendationError" to "Review the first blocking error in the round logic first.", + "tournaments.stageFlowRecommendationFixes" to "The typical configuration problems can be cleaned up automatically right away.", + "tournaments.stageFlowRecommendationMissingRule" to "A complete rule is still missing for {label}. That is the best place to continue.", + "tournaments.stageFlowRecommendationSave" to "The round logic is coherent. You can save the configuration now.", + "tournaments.stageFlowRecommendationTitle" to "Recommended next step", + "tournaments.stageFlowRecommendationWarnings" to "There are still warnings that should be reviewed before saving.", + "tournaments.stageFlowWithIntermediate" to "Preliminary round -> intermediate round ({stage2}) -> final round ({final})", + "tournaments.stageParticipantsTransferred" to "Qualified players were moved into {round}.", + "tournaments.stageStep1" to "Step 1", + "tournaments.stageStep1Description" to "First decide whether there should be an additional round after the preliminary round.", + "tournaments.stageStep1Title" to "Decide on an intermediate round", + "tournaments.stageStep2" to "Step 2", + "tournaments.stageStep2Description" to "Define what the intermediate round should look like and how many groups it needs.", + "tournaments.stageStep3" to "Step 3", + "tournaments.stageValidationApplyAllFixes" to "Apply {count} quick fix(es)", + "tournaments.stageValidationApplyFix" to "Apply", + "tournaments.stageValidationApplyTargetGroupFix" to "Set to {count} target groups", + "tournaments.stageValidationApplyTargetTypeFix" to "Switch to {target}", + "tournaments.stageValidationDescription" to "Fix these points before saving or moving players into the next round.", + "tournaments.stageValidationGroupCountRequired" to "Please set at least one valid group count.", + "tournaments.stageValidationKnockoutByesLikely" to "With an expected {count} qualifiers, the knockout bracket will very likely include byes.", + "tournaments.stageValidationKnockoutNeedsTwoPlayers" to "A knockout bracket needs at least 2 qualified players.", + "tournaments.stageValidationKnockoutTargetOnly" to "For a knockout final round, only the knockout bracket is allowed as the target here.", + "tournaments.stageValidationNoRules" to "There is no advancement rule yet for {stage}.", + "tournaments.stageValidationNotEnoughQualifiersForGroups" to "With only {count} qualifiers for {groups} target groups, empty groups would be created.", + "tournaments.stageValidationRulePlacesDuplicate" to "A single rule must not contain the same place more than once.", + "tournaments.stageValidationRulePlacesGap" to "The place definition has gaps. Usually a continuous order such as 1,2 or 1,2,3 is intended.", + "tournaments.stageValidationRulePlacesInvalid" to "The place definition is invalid. Only positive whole numbers such as 1 or 1,2 are allowed.", + "tournaments.stageValidationRulePlacesRequired" to "Enter at least one place that should advance.", + "tournaments.stageValidationRulesOverlap" to "Place {place} is assigned twice, in rule {first} and rule {second}.", + "tournaments.stageValidationSaveBlocked" to "Please fix the highlighted configuration errors first.", + "tournaments.stageValidationSuggestion" to "Suggestion: {value}", + "tournaments.stageValidationTargetGroupCountMismatch" to "The number of target groups must match the target round. Expected: {expected}.", + "tournaments.stageValidationTargetGroupsRequired" to "Please enter a valid number of target groups.", + "tournaments.stageValidationTargetTypeMismatch" to "The target of this rule must match the target round. Expected: {target}.", + "tournaments.stageValidationThinGroupsLikely" to "With {count} qualifiers across {groups} target groups, the groups will likely be very small.", + "tournaments.stageValidationTitle" to "Review configuration", + "tournaments.startKORound" to "K.o.-Runde starten", + "tournaments.statusActionAssign" to "Zuweisen", + "tournaments.statusActionCreate" to "Erstellen", + "tournaments.statusActionGenerate" to "Erzeugen", + "tournaments.statusActionPair" to "Pair", + "tournaments.statusActionReview" to "Prüfen", + "tournaments.statusActionStart" to "Starten", + "tournaments.statusConfigIncomplete" to "Konfiguration unvollständig", + "tournaments.statusConfigReady" to "Konfiguration bereit", + "tournaments.statusFinished" to "已完成", + "tournaments.statusGroupsMissing" to "Gruppen noch nicht erstellt", + "tournaments.statusGroupsReady" to "Gruppen bereit", + "tournaments.statusGroupsRunning" to "Gruppenphase läuft", + "tournaments.statusKnockoutReady" to "K.-o.-Runde startklar", + "tournaments.statusKnockoutRunning" to "K.-o.-Runde läuft", + "tournaments.statusLive" to "进行中", + "tournaments.statusOpen" to "未开始", + "tournaments.statusParticipantsConflicts" to "{count} Teilnehmer mit Konflikten", + "tournaments.statusParticipantsReady" to "{count} Teilnehmer konfliktfrei", + "tournaments.statusParticipantsUnassigned" to "{count} ohne Klasse", + "tournaments.statusTournamentCompleted" to "Turnier abgeschlossen", + "tournaments.statusUnpairedDoubles" to "{count} doubles without partner", + "tournaments.strategy" to "Strategie", + "tournaments.tabConfig" to "Konfiguration", + "tournaments.tabGroups" to "Gruppen", + "tournaments.table" to "球台", + "tournaments.tablesDistributed" to "球台已分配。", + "tournaments.tabParticipants" to "Teilnehmer", + "tournaments.tabPlacements" to "Platzierungen", + "tournaments.tabResults" to "Ergebnisse", + "tournaments.target" to "目标", + "tournaments.targetClass" to "Ziel", + "tournaments.targetGroupCount" to "目标小组数量", + "tournaments.targetGroupsLabel" to "{count} groups", + "tournaments.tournamentClassGenderFemale" to "女子", + "tournaments.tournamentClassGenderOpen" to "公开组(全体)", + "tournaments.tournamentName" to "Turniername", + "tournaments.tournamentParticipations" to "Turnierteilnahmen", + "tournaments.transferQualifiedToFinalFromIntermediate" to "Move qualified players into the final round", + "tournaments.transferQualifiedToFinalFromIntermediateDesc" to "Uses the intermediate-round to final-round rules and moves the qualified players across.", + "tournaments.transferQualifiedToFinalFromPreliminary" to "Move qualified players straight into the final round", + "tournaments.transferQualifiedToFinalFromPreliminaryDesc" to "Uses the preliminary-round to final-round rules and creates the qualified players directly there.", + "tournaments.transferQualifiedToIntermediate" to "Move qualified players into the intermediate round", + "tournaments.transferQualifiedToIntermediateDesc" to "Uses the preliminary-round to intermediate-round rules and moves the qualified players across.", + "tournaments.unknown" to "Unbekannt", + "tournaments.unknownDate" to "Unbekanntes Datum", + "tournaments.unmarkMatchLive" to "移除进行中标记", + "tournaments.unpairedDoubles" to "双打未配对", + "tournaments.useIntermediateStage" to "使用中间轮", + "tournaments.warningBirthDateMissing" to "该组别缺少出生日期", + "tournaments.warningClassMissing" to "未找到组别", + "tournaments.warningGenderMismatch" to "性别与组别不匹配", + "tournaments.warningGenderMissing" to "该组别缺少性别信息", + "tournaments.warningMissingPairing" to "缺少双打搭档", + "tournaments.warningTooOldForClass" to "对该组别来说年龄过大", + "tournaments.warningTooYoungForClass" to "对该组别来说年龄过小", + "tournaments.winningSets" to "Gewinnsätze", + "tournaments.withoutClass" to "Ohne Klasse", + "tournaments.workspaceProblemsTitle" to "{count} open issues", + "trainingDetails.birthdate" to "Geburtsdatum", + "trainingDetails.birthYear" to "Geburtsjahr", + "trainingDetails.last12Months" to "Letzte 12 Monate", + "trainingDetails.last3Months" to "Letzte 3 Monate", + "trainingDetails.maxBirthYear" to "Geboren ≤ Jahr", + "trainingDetails.noTrainings" to "Keine Trainingsteilnahmen vorhanden", + "trainingDetails.title" to "Trainings-Details", + "trainingDetails.total" to "Gesamt", + "trainingDetails.trainingParticipations" to "Trainingsteilnahmen (absteigend sortiert)", + "trainingGroupsTab.addMember" to "Mitglied hinzufügen...", + "trainingGroupsTab.cancel" to "Abbrechen", + "trainingGroupsTab.create" to "Erstellen", + "trainingGroupsTab.delete" to "Löschen", + "trainingGroupsTab.edit" to "Bearbeiten", + "trainingGroupsTab.editGroup" to "Gruppe bearbeiten", + "trainingGroupsTab.groupName" to "Gruppenname", + "trainingGroupsTab.groups" to "Gruppen", + "trainingGroupsTab.members" to "Mitglieder", + "trainingGroupsTab.newGroup" to "+ Neue Gruppe", + "trainingGroupsTab.noMembersAvailable" to "Keine Mitglieder verfügbar", + "trainingGroupsTab.remove" to "Entfernen", + "trainingStats.actions" to "操作", + "trainingStats.activeMembers" to "活跃成员", + "trainingStats.allTrainingDays" to "所有训练日", + "trainingStats.attendingMembers" to "出席成员", + "trainingStats.averageParticipationCurrentMonth" to "平均参与人数(本月)", + "trainingStats.averageParticipationHalfYear" to "平均参与人数(半年)", + "trainingStats.averageParticipationLastMonth" to "平均参与人数(上月)", + "trainingStats.averageParticipationQuarter" to "平均参与人数(季度)", + "trainingStats.averageParticipationYear" to "平均参与人数(全年)", + "trainingStats.birthdate" to "出生日期", + "trainingStats.date" to "日期", + "trainingStats.lastTraining" to "最近训练", + "trainingStats.memberParticipations" to "成员参与次数", + "trainingStats.name" to "姓名", + "trainingStats.noParticipants" to "无参与者", + "trainingStats.participants" to "参与者", + "trainingStats.participations12Months" to "参与次数(12 个月)", + "trainingStats.participations3Months" to "参与次数(3 个月)", + "trainingStats.participationsTotal" to "参与次数(总计)", + "trainingStats.qttr" to "QTTR", + "trainingStats.showDetails" to "显示详情", + "trainingStats.title" to "训练统计", + "trainingStats.trainingDayFilter" to "训练日", + "trainingStats.trainingDays" to "训练日(近 12 个月)", + "trainingStats.ttr" to "TTR", + "trainingStats.weekday" to "星期", + "trainingTimesTab.addTime" to "+ Zeit hinzufügen", + "trainingTimesTab.cancel" to "Abbrechen", + "trainingTimesTab.create" to "Erstellen", + "trainingTimesTab.delete" to "Löschen", + "trainingTimesTab.edit" to "Bearbeiten", + "trainingTimesTab.editTime" to "Trainingszeit bearbeiten", + "trainingTimesTab.friday" to "Freitag", + "trainingTimesTab.from" to "Von:", + "trainingTimesTab.loading" to "Lade Trainingszeiten...", + "trainingTimesTab.monday" to "Montag", + "trainingTimesTab.noTimes" to "Keine Trainingszeiten definiert", + "trainingTimesTab.saturday" to "Samstag", + "trainingTimesTab.save" to "Speichern", + "trainingTimesTab.saveError" to "Fehler beim Speichern der Trainingszeit", + "trainingTimesTab.sunday" to "Sonntag", + "trainingTimesTab.thursday" to "Donnerstag", + "trainingTimesTab.to" to "Bis:", + "trainingTimesTab.tuesday" to "Dienstag", + "trainingTimesTab.wednesday" to "Mittwoch", + "trainingTimesTab.weekday" to "Wochentag:", + "unknown" to "Unbekannt", + ) } + + private val fallback: Map get() = de + + fun get(languageCode: String, key: String, fallbackValue: String): String = + mapFor(languageCode)[key] ?: fallback[key] ?: fallbackValue + + private fun mapFor(languageCode: String): Map = when (languageCode) { + "de-CH" -> de_CH + "de-extended" -> de_extended + "de" -> de + "en-AU" -> en_AU + "en-GB" -> en_GB + "en-US" -> en_US + "es" -> es + "fil" -> fil + "fr" -> fr + "it" -> it + "ja" -> ja + "pl" -> pl + "th" -> th + "tl" -> tl + "zh" -> zh + else -> fallback + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/README.md b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/README.md new file mode 100644 index 00000000..81fb467e --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/README.md @@ -0,0 +1,13 @@ +# i18n + +Source of truth is the webapp locale JSONs in `frontend/src/i18n/locales/`. + +The mobile app generates a KMP-safe translation bundle into this package: +- `MobileStrings.kt` (generated) + +German (`de.json`) is the canonical key set. Missing keys in other locales are filled with German fallback values during generation so every supported mobile language has the same keys. + +Generate via: +```bash +node scripts/generate-mobile-i18n.js +``` diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthManager.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthManager.kt new file mode 100644 index 00000000..d4b44e1f --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthManager.kt @@ -0,0 +1,63 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.AuthApi +import de.tt_tagebuch.shared.api.SessionApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class AuthManager( + private val tokenProvider: MutableTokenProvider, + private val tokenStorage: TokenStorage, + private val authApi: AuthApi, + private val sessionApi: SessionApi, +) : TokenProvider { + + private val _state = MutableStateFlow(AuthState(isHydrating = true)) + val state: StateFlow = _state.asStateFlow() + + override val token: String? get() = tokenProvider.token + override val username: String? get() = tokenProvider.username + + suspend fun hydrate() { + _state.value = _state.value.copy(isHydrating = true) + try { + val stored = tokenStorage.load() + if (stored != null) { + tokenProvider.token = stored.token + tokenProvider.username = stored.username + _state.value = AuthState(token = stored.token, username = stored.username, isHydrating = true) + + val status = runCatching { sessionApi.status() }.getOrNull() + if (status != null && !status.valid) { + clearLocal() + } + } else { + clearLocal() + } + } finally { + _state.value = _state.value.copy(isHydrating = false) + } + } + + suspend fun login(email: String, password: String) { + val response = authApi.login(email = email, password = password) + val tokens = AuthTokens(token = response.token, username = email) + tokenProvider.token = tokens.token + tokenProvider.username = tokens.username + tokenStorage.save(tokens) + _state.value = AuthState(token = tokens.token, username = tokens.username, isHydrating = false) + } + + suspend fun logout() { + runCatching { authApi.logout() } + clearLocal() + } + + suspend fun clearLocal() { + tokenProvider.token = null + tokenProvider.username = null + tokenStorage.clear() + _state.value = AuthState(token = null, username = null, isHydrating = false) + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthState.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthState.kt new file mode 100644 index 00000000..4553c709 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthState.kt @@ -0,0 +1,10 @@ +package de.tt_tagebuch.shared.state + +data class AuthState( + val token: String? = null, + val username: String? = null, + val isHydrating: Boolean = false, +) { + val isLoggedIn: Boolean get() = !token.isNullOrBlank() +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthTokens.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthTokens.kt new file mode 100644 index 00000000..33be167e --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/AuthTokens.kt @@ -0,0 +1,7 @@ +package de.tt_tagebuch.shared.state + +data class AuthTokens( + val token: String, + val username: String, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubManager.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubManager.kt new file mode 100644 index 00000000..87be2264 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubManager.kt @@ -0,0 +1,90 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.ClubsApi +import de.tt_tagebuch.shared.api.PermissionsApi +import de.tt_tagebuch.shared.api.models.Club +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class ClubManager( + private val clubStorage: ClubStorage, + private val clubsApi: ClubsApi, + private val permissionsApi: PermissionsApi, +) { + private val _state = MutableStateFlow(ClubState()) + val state: StateFlow = _state.asStateFlow() + + suspend fun hydrate() { + val stored = clubStorage.loadCurrentClubId() + _state.value = _state.value.copy(currentClubId = stored) + } + + suspend fun loadClubs() { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + val clubs = clubsApi.listClubs() + _state.value = _state.value.copy(clubs = clubs, isLoading = false) + } catch (t: Throwable) { + if (t is CancellationException) throw t + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Fehler beim Laden der Clubs")) + } + } + + suspend fun createClub(name: String) { + val trimmed = name.trim() + if (trimmed.isEmpty()) return + _state.value = _state.value.copy(isLoading = true, error = null) + try { + val newClub = clubsApi.createClub(trimmed) + loadClubs() + selectClub(newClub.id) + } catch (t: Throwable) { + if (t is CancellationException) throw t + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Verein konnte nicht erstellt werden")) + } + } + + suspend fun fetchClubDetail(clubId: Int): Club { + return clubsApi.getClub(clubId) + } + + suspend fun selectClub(clubId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + val permissions = permissionsApi.getUserPermissions(clubId) + clubStorage.saveCurrentClubId(clubId) + _state.value = _state.value.copy( + currentClubId = clubId, + currentPermissions = permissions, + isLoading = false, + ) + } catch (t: Throwable) { + if (t is CancellationException) throw t + _state.value = _state.value.copy( + isLoading = false, + error = t.toUserMessage("Keine Berechtigung oder Fehler beim Laden der Permissions"), + currentPermissions = null, + currentClubId = null, + ) + clubStorage.saveCurrentClubId(null) + } + } + + suspend fun requestAccess(clubId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + clubsApi.requestAccess(clubId) + _state.value = _state.value.copy(isLoading = false) + } catch (t: Throwable) { + if (t is CancellationException) throw t + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Access request fehlgeschlagen")) + } + } + + suspend fun clearSelection() { + clubStorage.saveCurrentClubId(null) + _state.value = _state.value.copy(currentClubId = null, currentPermissions = null) + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubState.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubState.kt new file mode 100644 index 00000000..e81ac88d --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubState.kt @@ -0,0 +1,13 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.models.Club +import de.tt_tagebuch.shared.api.models.UserClubPermissions + +data class ClubState( + val clubs: List = emptyList(), + val currentClubId: Int? = null, + val currentPermissions: UserClubPermissions? = null, + val isLoading: Boolean = false, + val error: String? = null, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubStorage.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubStorage.kt new file mode 100644 index 00000000..6e3800c2 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ClubStorage.kt @@ -0,0 +1,7 @@ +package de.tt_tagebuch.shared.state + +interface ClubStorage { + suspend fun loadCurrentClubId(): Int? + suspend fun saveCurrentClubId(clubId: Int?) +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryManager.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryManager.kt new file mode 100644 index 00000000..d5f59d29 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryManager.kt @@ -0,0 +1,303 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.AccidentApi +import de.tt_tagebuch.shared.api.DiaryApi +import de.tt_tagebuch.shared.api.DiaryMemberActivitiesApi +import de.tt_tagebuch.shared.api.DiaryMemberApi +import de.tt_tagebuch.shared.api.GroupApi +import de.tt_tagebuch.shared.api.MemberGroupPhotosApi +import de.tt_tagebuch.shared.api.ParticipantsApi +import de.tt_tagebuch.shared.api.PredefinedActivitiesApi +import de.tt_tagebuch.shared.api.models.AccidentReportDto +import de.tt_tagebuch.shared.api.models.AddDiaryMemberNoteBody +import de.tt_tagebuch.shared.api.models.CreateAccidentBody +import de.tt_tagebuch.shared.api.models.DiaryMemberNoteDto +import de.tt_tagebuch.shared.api.models.DiaryMemberTagLinkDto +import de.tt_tagebuch.shared.api.models.DiaryMemberTagMutationBody +import de.tt_tagebuch.shared.api.models.DiaryTag +import de.tt_tagebuch.shared.api.models.PredefinedActivityDto +import de.tt_tagebuch.shared.api.models.AddDiaryPlanGroupActivityRequest +import de.tt_tagebuch.shared.api.models.CreateDiaryPlanActivityRequest +import de.tt_tagebuch.shared.api.models.CreateTrainingGroupBody +import de.tt_tagebuch.shared.api.models.DeleteTrainingGroupBody +import de.tt_tagebuch.shared.api.models.DiaryDateActivityItem +import de.tt_tagebuch.shared.api.models.DiaryFreeformActivity +import de.tt_tagebuch.shared.api.models.DiaryMemberActivityLink +import de.tt_tagebuch.shared.api.models.MemberGroupPhotoDto +import de.tt_tagebuch.shared.api.models.DiaryPlanGroup +import de.tt_tagebuch.shared.api.models.DiaryTrainingParticipant +import de.tt_tagebuch.shared.api.models.UpdateDiaryPlanActivityRequest +import de.tt_tagebuch.shared.api.models.UpdateNestedPlanGroupActivityRequest +import de.tt_tagebuch.shared.api.models.UpdateTrainingGroupBody +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class DiaryManager( + private val diaryApi: DiaryApi, + private val participantsApi: ParticipantsApi, + private val groupApi: GroupApi, + private val diaryMemberActivitiesApi: DiaryMemberActivitiesApi, + private val diaryMemberApi: DiaryMemberApi, + private val predefinedActivitiesApi: PredefinedActivitiesApi, + private val accidentApi: AccidentApi, + private val memberGroupPhotosApi: MemberGroupPhotosApi, +) { + private val _state = MutableStateFlow(DiaryState()) + val state: StateFlow = _state.asStateFlow() + + suspend fun fetchDateActivities(clubId: Int, diaryDateId: Int): List { + return diaryApi.listDateActivities(clubId, diaryDateId) + } + + suspend fun listFreeformActivities(diaryDateId: Int): List { + return diaryApi.listFreeformActivities(diaryDateId) + } + + suspend fun addFreeformActivity(diaryDateId: Int, description: String): DiaryFreeformActivity { + return diaryApi.addFreeformActivity(diaryDateId, description) + } + + suspend fun listMemberActivityLinks(clubId: Int, planOrGroupActivityId: Int): List { + return diaryMemberActivitiesApi.list(clubId, planOrGroupActivityId) + } + + suspend fun addParticipantsToMemberActivity(clubId: Int, planOrGroupActivityId: Int, participantRowIds: List) { + diaryMemberActivitiesApi.add(clubId, planOrGroupActivityId, participantRowIds) + } + + suspend fun removeParticipantFromMemberActivity(clubId: Int, planOrGroupActivityId: Int, participantRowId: Int) { + diaryMemberActivitiesApi.remove(clubId, planOrGroupActivityId, participantRowId) + } + + suspend fun listAllDiaryTags(): List { + return diaryApi.listTags() + } + + suspend fun listMemberNotes(clubId: Int, diaryDateId: Int, memberId: Int): List { + return diaryMemberApi.listNotes(clubId, diaryDateId, memberId) + } + + suspend fun addMemberNote(clubId: Int, diaryDateId: Int, memberId: Int, content: String): List { + return diaryMemberApi.addNote( + clubId, + AddDiaryMemberNoteBody(memberId = memberId, diaryDateId = diaryDateId, content = content.trim()), + ) + } + + suspend fun deleteMemberNote(clubId: Int, diaryDateId: Int, memberId: Int, noteId: Int): List { + return diaryMemberApi.deleteNote(clubId, noteId, diaryDateId, memberId) + } + + suspend fun listMemberTags(clubId: Int, diaryDateId: Int, memberId: Int): List { + return diaryMemberApi.listTags(clubId, diaryDateId, memberId) + } + + suspend fun addMemberTag(clubId: Int, diaryDateId: Int, memberId: Int, tagId: Int): List { + return diaryMemberApi.addTag(clubId, DiaryMemberTagMutationBody(diaryDateId, memberId, tagId)) + } + + suspend fun removeMemberTag(clubId: Int, diaryDateId: Int, memberId: Int, tagId: Int): List { + return diaryMemberApi.removeTag(clubId, DiaryMemberTagMutationBody(diaryDateId, memberId, tagId)) + } + + suspend fun createMemberTagAndLink(clubId: Int, diaryDateId: Int, memberId: Int, name: String): List { + val tag = diaryApi.createTag(name.trim()) + return diaryMemberApi.addTag(clubId, DiaryMemberTagMutationBody(diaryDateId, memberId, tag.id)) + } + + suspend fun searchPredefinedActivities(query: String, limit: Int = 20): List { + return predefinedActivitiesApi.search(query, limit) + } + + suspend fun listPredefinedActivities(scope: String? = null): List { + return predefinedActivitiesApi.list(scope) + } + + suspend fun getPredefinedActivity(id: Int): PredefinedActivityDto { + return predefinedActivitiesApi.getById(id) + } + + suspend fun listAccidents(clubId: Int, diaryDateId: Int): List { + return accidentApi.list(clubId, diaryDateId) + } + + suspend fun addAccident(clubId: Int, diaryDateId: Int, memberId: Int, description: String) { + accidentApi.create( + CreateAccidentBody( + clubId = clubId, + memberId = memberId, + diaryDateId = diaryDateId, + accident = description.trim(), + ), + ) + } + + suspend fun listMemberGroupPhotos(clubId: Int): List { + return memberGroupPhotosApi.list(clubId) + } + + suspend fun uploadMemberGroupPhoto(clubId: Int, imageBytes: ByteArray, title: String, description: String) { + memberGroupPhotosApi.upload(clubId, imageBytes, title, description) + } + + suspend fun deleteMemberGroupPhoto(clubId: Int, photoId: Int) { + memberGroupPhotosApi.delete(clubId, photoId) + } + + /** Liefert die `Participant`-Zeilen-ID für das Mitglied am Tag, legt bei Bedarf „anwesend“ an. */ + suspend fun ensureParticipantRowId(diaryDateId: Int, memberId: Int): Int { + val list = participantsApi.listForDate(diaryDateId) + list.find { it.memberId == memberId }?.id?.let { return it } + return participantsApi.add(diaryDateId, memberId).id + } + + suspend fun createPlanActivity(clubId: Int, body: CreateDiaryPlanActivityRequest) { + diaryApi.createDateActivity(clubId, body) + } + + suspend fun addPlanGroupActivity(body: AddDiaryPlanGroupActivityRequest) { + diaryApi.addDateGroupActivity(body) + } + + suspend fun updatePlanActivity(clubId: Int, activityId: Int, body: UpdateDiaryPlanActivityRequest) { + diaryApi.updateDateActivity(clubId, activityId, body) + } + + suspend fun updatePlanActivityOrder(clubId: Int, activityId: Int, orderId: Int) { + diaryApi.updateDateActivityOrder(clubId, activityId, orderId) + } + + suspend fun deletePlanActivity(clubId: Int, activityId: Int) { + diaryApi.deleteDateActivity(clubId, activityId) + } + + suspend fun updatePlanNestedGroupActivity(clubId: Int, groupActivityId: Int, body: UpdateNestedPlanGroupActivityRequest) { + diaryApi.updateNestedGroupActivity(clubId, groupActivityId, body) + } + + suspend fun deletePlanNestedGroupActivity(clubId: Int, groupActivityId: Int) { + diaryApi.deleteNestedGroupActivity(clubId, groupActivityId) + } + + suspend fun listTrainingGroups(clubId: Int, diaryDateId: Int): List { + return groupApi.listForDiaryDate(clubId, diaryDateId) + } + + suspend fun createTrainingGroup(clubId: Int, diaryDateId: Int, name: String, lead: String?) { + groupApi.create(CreateTrainingGroupBody(clubid = clubId, dateid = diaryDateId, name = name, lead = lead)) + } + + suspend fun updateTrainingGroup(groupId: Int, clubId: Int, diaryDateId: Int, name: String, lead: String?) { + groupApi.update(groupId, UpdateTrainingGroupBody(clubid = clubId, dateid = diaryDateId, name = name, lead = lead)) + } + + suspend fun deleteTrainingGroup(groupId: Int, clubId: Int, diaryDateId: Int) { + groupApi.delete(groupId, DeleteTrainingGroupBody(clubid = clubId, dateid = diaryDateId)) + } + + suspend fun listTrainingParticipants(diaryDateId: Int): List { + return participantsApi.listForDate(diaryDateId) + } + + suspend fun addTrainingParticipant(diaryDateId: Int, memberId: Int) { + participantsApi.add(diaryDateId, memberId) + } + + suspend fun removeTrainingParticipant(diaryDateId: Int, memberId: Int) { + participantsApi.remove(diaryDateId, memberId) + } + + suspend fun setTrainingParticipantAttendanceStatus(diaryDateId: Int, memberId: Int, attendanceStatus: String) { + participantsApi.updateAttendanceStatus(diaryDateId, memberId, attendanceStatus) + } + + suspend fun setTrainingParticipantGroup(diaryDateId: Int, memberId: Int, groupId: Int?) { + participantsApi.updateParticipantGroup(diaryDateId, memberId, groupId) + } + + suspend fun loadDates(clubId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + val dates = diaryApi.listDates(clubId) + _state.value = _state.value.copy(isLoading = false, dates = dates) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Fehler beim Laden des Tagebuchs")) + } + } + + suspend fun createDate(clubId: Int, date: String, trainingStart: String?, trainingEnd: String?) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + diaryApi.createDate(clubId, date, trainingStart, trainingEnd) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Eintrag konnte nicht erstellt werden")) + } + } + + suspend fun updateTimes(clubId: Int, dateId: Int, trainingStart: String?, trainingEnd: String?) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + diaryApi.updateTimes(clubId, dateId, trainingStart, trainingEnd) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Zeiten konnten nicht gespeichert werden")) + } + } + + suspend fun deleteDate(clubId: Int, dateId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + diaryApi.deleteDate(clubId, dateId) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Eintrag konnte nicht gelöscht werden")) + } + } + + suspend fun addNote(clubId: Int, diaryDateId: Int, content: String) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + diaryApi.addNote(diaryDateId, content) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Notiz konnte nicht gespeichert werden")) + } + } + + suspend fun deleteNote(clubId: Int, noteId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + diaryApi.deleteNote(noteId) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Notiz konnte nicht gelöscht werden")) + } + } + + suspend fun createAndLinkTag(clubId: Int, diaryDateId: Int, name: String) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + val tag = diaryApi.createTag(name) + diaryApi.linkTag(clubId, diaryDateId, tag.id) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Tag konnte nicht gespeichert werden")) + } + } + + suspend fun removeTag(clubId: Int, tagId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + diaryApi.removeTag(clubId, tagId) + loadDates(clubId) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Tag konnte nicht entfernt werden")) + } + } + + fun clear() { + _state.value = DiaryState() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryState.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryState.kt new file mode 100644 index 00000000..d310e07d --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/DiaryState.kt @@ -0,0 +1,10 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.models.DiaryDate + +data class DiaryState( + val dates: List = emptyList(), + val isLoading: Boolean = false, + val error: String? = null, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ErrorMapper.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ErrorMapper.kt new file mode 100644 index 00000000..fa20d55e --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/ErrorMapper.kt @@ -0,0 +1,27 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.http.ApiException + +internal fun Throwable.toUserMessage(fallback: String): String { + if (this is ApiException) { + return when (statusCode) { + 401 -> "Session abgelaufen. Bitte erneut anmelden." + 403 -> "Keine Berechtigung für diese Aktion." + else -> message ?: fallback + } + } + + val rawMessage = message.orEmpty() + val normalizedMessage = rawMessage.lowercase() + if ( + normalizedMessage.contains("failed to connect") || + normalizedMessage.contains("unable to resolve") || + normalizedMessage.contains("connection refused") || + normalizedMessage.contains("timeout") || + normalizedMessage.contains("network") + ) { + return "Keine Netzwerkverbindung zum Backend. Bitte Verbindung und Backend-URL prüfen." + } + + return rawMessage.ifBlank { fallback } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageManager.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageManager.kt new file mode 100644 index 00000000..8c9c5ded --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageManager.kt @@ -0,0 +1,27 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.i18n.MobileStrings +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class LanguageManager( + private val languageStorage: LanguageStorage, +) { + private val _state = MutableStateFlow(LanguageState()) + val state: StateFlow = _state.asStateFlow() + + suspend fun hydrate() { + val stored = languageStorage.loadLanguageCode() + val languageCode = stored?.takeIf { code -> + MobileStrings.supportedLanguages.any { it.code == code } + } ?: MobileStrings.DEFAULT_LANGUAGE + _state.value = LanguageState(languageCode) + } + + suspend fun selectLanguage(languageCode: String) { + if (MobileStrings.supportedLanguages.none { it.code == languageCode }) return + languageStorage.saveLanguageCode(languageCode) + _state.value = LanguageState(languageCode) + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageState.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageState.kt new file mode 100644 index 00000000..67a21359 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageState.kt @@ -0,0 +1,7 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.i18n.MobileStrings + +data class LanguageState( + val currentLanguageCode: String = MobileStrings.DEFAULT_LANGUAGE, +) diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageStorage.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageStorage.kt new file mode 100644 index 00000000..3f812f50 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/LanguageStorage.kt @@ -0,0 +1,6 @@ +package de.tt_tagebuch.shared.state + +interface LanguageStorage { + suspend fun loadLanguageCode(): String? + suspend fun saveLanguageCode(languageCode: String) +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersManager.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersManager.kt new file mode 100644 index 00000000..8f2931a9 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersManager.kt @@ -0,0 +1,69 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.MemberActivitiesApi +import de.tt_tagebuch.shared.api.MembersApi +import de.tt_tagebuch.shared.api.TrainingGroupsApi +import de.tt_tagebuch.shared.api.TrainingTimesApi +import de.tt_tagebuch.shared.api.models.Member +import de.tt_tagebuch.shared.api.models.MemberActivityStatDto +import de.tt_tagebuch.shared.api.models.MemberLastParticipationDto +import de.tt_tagebuch.shared.api.models.MemberSetBody +import de.tt_tagebuch.shared.api.models.TrainingGroupDto +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class MembersManager( + private val membersApi: MembersApi, + private val trainingGroupsApi: TrainingGroupsApi, + private val memberActivitiesApi: MemberActivitiesApi, + private val trainingTimesApi: TrainingTimesApi, +) { + private val _state = MutableStateFlow(MembersState()) + val state: StateFlow = _state.asStateFlow() + + suspend fun loadMembers(clubId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + val members = membersApi.listMembers(clubId) + .sortedWith(compareBy { it.lastName.lowercase() }.thenBy { it.firstName.lowercase() }) + _state.value = _state.value.copy(members = members, isLoading = false) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Mitglieder konnten nicht geladen werden")) + } + } + + suspend fun saveMember(clubId: Int, body: MemberSetBody) { + membersApi.setMember(clubId, body) + } + + suspend fun uploadMemberPortrait(clubId: Int, memberId: Int, imageBytes: ByteArray) { + membersApi.uploadMemberImage(clubId, memberId, imageBytes, makePrimary = true) + } + + suspend fun listTrainingGroups(clubId: Int): List = trainingGroupsApi.listGroups(clubId) + + suspend fun listMemberTrainingGroups(clubId: Int, memberId: Int): List = + trainingGroupsApi.listMemberGroups(clubId, memberId) + + suspend fun addMemberToTrainingGroup(clubId: Int, groupId: Int, memberId: Int) { + trainingGroupsApi.addMemberToGroup(clubId, groupId, memberId) + } + + suspend fun removeMemberFromTrainingGroup(clubId: Int, groupId: Int, memberId: Int) { + trainingGroupsApi.removeMemberFromGroup(clubId, groupId, memberId) + } + + suspend fun memberActivityStats(clubId: Int, memberId: Int, period: String = "year"): List = + memberActivitiesApi.listActivityStats(clubId, memberId, period) + + suspend fun memberLastParticipations(clubId: Int, memberId: Int, limit: Int = 12): List = + memberActivitiesApi.listLastParticipations(clubId, memberId, limit) + + suspend fun trainingScheduleGroups(clubId: Int): List = + trainingTimesApi.listGroupsWithTimes(clubId) + + fun clear() { + _state.value = MembersState() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersState.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersState.kt new file mode 100644 index 00000000..fd220438 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MembersState.kt @@ -0,0 +1,10 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.models.Member + +data class MembersState( + val members: List = emptyList(), + val isLoading: Boolean = false, + val error: String? = null, +) + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MutableTokenProvider.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MutableTokenProvider.kt new file mode 100644 index 00000000..dbf4e72a --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/MutableTokenProvider.kt @@ -0,0 +1,6 @@ +package de.tt_tagebuch.shared.state + +class MutableTokenProvider : TokenProvider { + override var token: String? = null + override var username: String? = null +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenProvider.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenProvider.kt new file mode 100644 index 00000000..f5e75298 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenProvider.kt @@ -0,0 +1,6 @@ +package de.tt_tagebuch.shared.state + +interface TokenProvider { + val token: String? + val username: String? +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenStorage.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenStorage.kt new file mode 100644 index 00000000..d1ddca40 --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TokenStorage.kt @@ -0,0 +1,8 @@ +package de.tt_tagebuch.shared.state + +interface TokenStorage { + suspend fun load(): AuthTokens? + suspend fun save(tokens: AuthTokens) + suspend fun clear() +} + diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsManager.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsManager.kt new file mode 100644 index 00000000..8d9eb0cc --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsManager.kt @@ -0,0 +1,26 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.TrainingStatsApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class TrainingStatsManager( + private val trainingStatsApi: TrainingStatsApi, +) { + private val _state = MutableStateFlow(TrainingStatsState()) + val state: StateFlow = _state.asStateFlow() + + suspend fun loadStats(clubId: Int) { + _state.value = _state.value.copy(isLoading = true, error = null) + try { + _state.value = _state.value.copy(stats = trainingStatsApi.getStats(clubId), isLoading = false) + } catch (t: Throwable) { + _state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Statistik konnte nicht geladen werden")) + } + } + + fun clear() { + _state.value = TrainingStatsState() + } +} diff --git a/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsState.kt b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsState.kt new file mode 100644 index 00000000..92dbdc7b --- /dev/null +++ b/mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/state/TrainingStatsState.kt @@ -0,0 +1,10 @@ +package de.tt_tagebuch.shared.state + +import de.tt_tagebuch.shared.api.models.TrainingStats + +data class TrainingStatsState( + val stats: TrainingStats? = null, + val isLoading: Boolean = false, + val error: String? = null, +) + diff --git a/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/api/http/IosHttpClientEngineFactory.kt b/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/api/http/IosHttpClientEngineFactory.kt new file mode 100644 index 00000000..5edd19b9 --- /dev/null +++ b/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/api/http/IosHttpClientEngineFactory.kt @@ -0,0 +1,9 @@ +package de.tt_tagebuch.shared.api.http + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.darwin.Darwin + +class IosHttpClientEngineFactory : HttpClientEngineFactory { + override fun create(): HttpClientEngine = Darwin.create() +} + diff --git a/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosClubStorage.kt b/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosClubStorage.kt new file mode 100644 index 00000000..5ceda0b4 --- /dev/null +++ b/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosClubStorage.kt @@ -0,0 +1,86 @@ +package de.tt_tagebuch.shared.state + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr +import kotlinx.cinterop.refTo +import kotlinx.cinterop.memcpy +import platform.CoreFoundation.CFDictionaryRef +import platform.CoreFoundation.CFTypeRefVar +import platform.Foundation.NSData +import platform.Foundation.NSString +import platform.Foundation.NSUTF8StringEncoding +import platform.Foundation.dataUsingEncoding +import platform.Security.SecItemAdd +import platform.Security.SecItemCopyMatching +import platform.Security.SecItemDelete +import platform.Security.errSecSuccess +import platform.Security.kSecAttrAccount +import platform.Security.kSecAttrService +import platform.Security.kSecClass +import platform.Security.kSecClassGenericPassword +import platform.Security.kSecMatchLimit +import platform.Security.kSecMatchLimitOne +import platform.Security.kSecReturnData +import platform.Security.kSecValueData + +class IosClubStorage : ClubStorage { + override suspend fun loadCurrentClubId(): Int? = withContext(Dispatchers.Default) { + loadString(KEY_CURRENT_CLUB_ID)?.toIntOrNull() + } + + override suspend fun saveCurrentClubId(clubId: Int?) = withContext(Dispatchers.Default) { + if (clubId == null) delete(KEY_CURRENT_CLUB_ID) else saveString(KEY_CURRENT_CLUB_ID, clubId.toString()) + } + + private fun loadString(key: String): String? = memScoped { + val query = keychainQuery( + account = key, + extra = mapOf( + kSecReturnData to true, + kSecMatchLimit to kSecMatchLimitOne, + ), + ) + + val result = alloc() + val status = SecItemCopyMatching(query as CFDictionaryRef, result.ptr) + if (status != errSecSuccess) return@memScoped null + + val data = result.value as? NSData ?: return@memScoped null + val bytes = data.bytes ?: return@memScoped null + val length = data.length.toInt() + val buffer = ByteArray(length) + memcpy(buffer.refTo(0), bytes, data.length) + buffer.decodeToString() + } + + private fun saveString(key: String, value: String) { + delete(key) + val data = (value as NSString).dataUsingEncoding(NSUTF8StringEncoding) ?: return + val query = keychainQuery( + account = key, + extra = mapOf(kSecValueData to data), + ) + SecItemAdd(query as CFDictionaryRef, null) + } + + private fun delete(key: String) { + SecItemDelete(keychainQuery(key) as CFDictionaryRef) + } + + private fun keychainQuery(account: String, extra: Map = emptyMap()): Map { + return mapOf( + kSecClass to kSecClassGenericPassword, + kSecAttrService to SERVICE_NAME, + kSecAttrAccount to account, + ) + extra + } + + private companion object { + private const val SERVICE_NAME = "de.tt_tagebuch.app" + private const val KEY_CURRENT_CLUB_ID = "currentClubId" + } +} + diff --git a/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosTokenStorage.kt b/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosTokenStorage.kt new file mode 100644 index 00000000..1b2e704a --- /dev/null +++ b/mobile-app/shared/src/iosMain/kotlin/de/tt_tagebuch/shared/state/IosTokenStorage.kt @@ -0,0 +1,94 @@ +package de.tt_tagebuch.shared.state + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr +import kotlinx.cinterop.refTo +import kotlinx.cinterop.memcpy +import platform.CoreFoundation.CFDictionaryRef +import platform.CoreFoundation.CFTypeRefVar +import platform.Foundation.NSData +import platform.Foundation.NSString +import platform.Foundation.NSUTF8StringEncoding +import platform.Foundation.dataUsingEncoding +import platform.Security.SecItemAdd +import platform.Security.SecItemCopyMatching +import platform.Security.SecItemDelete +import platform.Security.errSecSuccess +import platform.Security.kSecAttrAccount +import platform.Security.kSecAttrService +import platform.Security.kSecClass +import platform.Security.kSecClassGenericPassword +import platform.Security.kSecMatchLimit +import platform.Security.kSecMatchLimitOne +import platform.Security.kSecReturnData +import platform.Security.kSecValueData + +class IosTokenStorage : TokenStorage { + override suspend fun load(): AuthTokens? = withContext(Dispatchers.Default) { + val token = loadString(KEY_TOKEN) + val username = loadString(KEY_USERNAME) + if (token.isNullOrBlank() || username.isNullOrBlank()) null else AuthTokens(token, username) + } + + override suspend fun save(tokens: AuthTokens) = withContext(Dispatchers.Default) { + saveString(KEY_TOKEN, tokens.token) + saveString(KEY_USERNAME, tokens.username) + } + + override suspend fun clear() = withContext(Dispatchers.Default) { + delete(KEY_TOKEN) + delete(KEY_USERNAME) + } + + private fun loadString(key: String): String? = memScoped { + val query = keychainQuery( + account = key, + extra = mapOf( + kSecReturnData to true, + kSecMatchLimit to kSecMatchLimitOne, + ), + ) + + val result = alloc() + val status = SecItemCopyMatching(query as CFDictionaryRef, result.ptr) + if (status != errSecSuccess) return@memScoped null + + val data = result.value as? NSData ?: return@memScoped null + val bytes = data.bytes ?: return@memScoped null + val length = data.length.toInt() + val buffer = ByteArray(length) + memcpy(buffer.refTo(0), bytes, data.length) + buffer.decodeToString() + } + + private fun saveString(key: String, value: String) { + delete(key) + val data = (value as NSString).dataUsingEncoding(NSUTF8StringEncoding) ?: return + val query = keychainQuery( + account = key, + extra = mapOf(kSecValueData to data), + ) + SecItemAdd(query as CFDictionaryRef, null) + } + + private fun delete(key: String) { + SecItemDelete(keychainQuery(key) as CFDictionaryRef) + } + + private fun keychainQuery(account: String, extra: Map = emptyMap()): Map { + return mapOf( + kSecClass to kSecClassGenericPassword, + kSecAttrService to SERVICE_NAME, + kSecAttrAccount to account, + ) + extra + } + + private companion object { + private const val SERVICE_NAME = "de.tt_tagebuch.app" + private const val KEY_TOKEN = "token" + private const val KEY_USERNAME = "username" + } +} diff --git a/mobile-app_legacy_rn_expo/.gitignore b/mobile-app_legacy_rn_expo/.gitignore new file mode 100644 index 00000000..d914c328 --- /dev/null +++ b/mobile-app_legacy_rn_expo/.gitignore @@ -0,0 +1,41 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +# generated native folders +/ios +/android diff --git a/mobile-app_legacy_rn_expo/App.tsx b/mobile-app_legacy_rn_expo/App.tsx new file mode 100644 index 00000000..28bf4e68 --- /dev/null +++ b/mobile-app_legacy_rn_expo/App.tsx @@ -0,0 +1,52 @@ +import 'react-native-gesture-handler'; + +import { StatusBar } from 'expo-status-bar'; +import { useEffect } from 'react'; +import { ActivityIndicator, StyleSheet, View } from 'react-native'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; + +import { AppNavigation } from './src/navigation/AppNavigation'; +import { AuthProvider, useAuth } from './src/state/auth/AuthContext'; +import './src/i18n'; + +export default function App() { + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, +}); + +function AppBootstrap() { + const { isHydrating, hydrate } = useAuth(); + + useEffect(() => { + hydrate(); + }, [hydrate]); + + if (isHydrating) { + return ( + + + + + ); + } + + return ( + <> + + + + ); +} diff --git a/mobile-app_legacy_rn_expo/README.md b/mobile-app_legacy_rn_expo/README.md new file mode 100644 index 00000000..1dc88a63 --- /dev/null +++ b/mobile-app_legacy_rn_expo/README.md @@ -0,0 +1,18 @@ +# Mobile App (Android) – React Native (Expo) + +This folder is a restart of the Android mobile app using React Native + Expo + TypeScript. + +## Auth model (important) +- The backend uses **JWT tokens**, not cookie sessions. +- Login: `POST /api/auth/login` returns `{ token }` +- The token is sent on requests via `authcode` header (same as the webapp). + +## Run +```bash +cd mobile-app +npm run android +``` + +## Backend URL +Set `BACKEND_BASE_URL` via Expo config `app.json` (extra) or use the default `http://localhost:3005`. + diff --git a/mobile-app_legacy_rn_expo/app.json b/mobile-app_legacy_rn_expo/app.json new file mode 100644 index 00000000..727c44cd --- /dev/null +++ b/mobile-app_legacy_rn_expo/app.json @@ -0,0 +1,34 @@ +{ + "expo": { + "name": "mobile-app", + "slug": "mobile-app", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "newArchEnabled": true, + "splash": { + "image": "./assets/splash-icon.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + }, + "edgeToEdgeEnabled": true, + "predictiveBackGestureEnabled": false + }, + "web": { + "favicon": "./assets/favicon.png" + }, + "plugins": [ + "expo-localization", + "expo-secure-store" + ] + } +} diff --git a/mobile-app_legacy_rn_expo/assets/adaptive-icon.png b/mobile-app_legacy_rn_expo/assets/adaptive-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03d6f6b6c6727954aec1d8206222769afd178d8d GIT binary patch literal 17547 zcmdVCc|4Ti*EoFcS?yF*_R&TYQOH(|sBGDq8KR;jni6eN$=oWm(;}%b6=4u1OB+)v zB_hpO3nh}szBBXQ)A#%Q-rw_nzR&Y~e}BB6&-?oL%*=hAbDeXpbDis4=UmHu*424~ ztdxor0La?g*}4M|u%85wz++!_Wz7$(_79;y-?M_2<8zbyZcLtE#X^ zL3MTA-+%1K|9ZqQu|lk*{_p=k%CXN{4CmuV><2~!1O20lm{dc<*Dqh%K7Vd(Zf>oq zsr&S)uA$)zpWj$jh0&@1^r>DTXsWAgZftC+umAFwk(g9L-5UhHwEawUMxdV5=IdKl9436TVl;2HG#c;&s>?qV=bZ<1G1 zGL92vWDII5F@*Q-Rgk(*nG6_q=^VO{)x0`lqq2GV~}@c!>8{Rh%N*#!Md zcK;8gf67wupJn>jNdIgNpZR|v@cIA03H<+(hK<+%dm4_({I~3;yCGk?+3uu{%&A)1 zP|cr?lT925PwRQ?kWkw`F7W*U9t!16S{OM(7PR?fkti+?J% z7t5SDGUlQrKxkX1{4X56^_wp&@p8D-UXyDn@OD!Neu1W6OE-Vp{U<+)W!P+q)zBy! z&z(NXdS(=_xBLY;#F~pon__oo^`e~z#+CbFrzoXRPOG}Nty51XiyX4#FXgyB7C9~+ zJiO_tZs0udqi(V&y>k5{-ZTz-4E1}^yLQcB{usz{%pqgzyG_r0V|yEqf`yyE$R)>* z+xu$G;G<(8ht7;~bBj=7#?I_I?L-p;lKU*@(E{93EbN=5lI zX1!nDlH@P$yx*N#<(=LojPrW6v$gn-{GG3wk1pnq240wq5w>zCpFLjjwyA1~#p9s< zV0B3aDPIliFkyvKZ0Pr2ab|n2-P{-d_~EU+tk(nym16NQ;7R?l}n==EP3XY7;&ok_M4wThw?=Qb2&IL0r zAa_W>q=IjB4!et=pWgJ$Km!5ZBoQtIu~QNcr*ea<2{!itWk|z~7Ga6;9*2=I4YnbG zXDOh~y{+b6-rN^!E?Uh7sMCeE(5b1)Y(vJ0(V|%Z+1|iAGa9U(W5Rfp-YkJ(==~F8 z4dcXe@<^=?_*UUyUlDslpO&B{T2&hdymLe-{x%w1HDxa-ER)DU(0C~@xT99v@;sM5 zGC{%ts)QA+J6*tjnmJk)fQ!Nba|zIrKJO8|%N$KG2&Z6-?Es7|UyjD6boZ~$L!fQ} z_!fV(nQ7VdVwNoANg?ob{)7Fg<`+;01YGn1eNfb_nJKrB;sLya(vT;Nm|DnCjoyTV zWG0|g2d3~Oy-D$e|w|reqyJ}4Ynk#J`ZSh$+7UESh|JJ z%E?JpXj^*PmAp-4rX?`Bh%1?y4R$^fg7A^LDl2zEqz@KfoRz*)d-&3ME4z3RecXF( z&VAj}EL`d22JTP~{^a_c`^!!rO9~#1rN``Vtu@^d~$&2DJ0 zI`*LVx=i7T@zn{|Ae&_LKU;BmoKcvu!U;XNLm?- z`9$AWwdIi*vT?H2j1QmM_$p!dZjaBkMBW#Pu*SPs+x=rj-rsZX*Uwl!jw##am$Sla z={ixqgTqq43kA2TwznpSACvKQ?_e*>7MqBphDh`@kC8vNX-atL-E9HOfm@-rwJ=!w zDy4O~H&p86Sz}lqM%YCejH?s7llrpn7o|E(7AL-qjJvf?n&W*AizC+tjmNU*K603| zOZctr603w>uzzZk8S@TPdM+BTjUhn)Om0Fx>)e6c&g69aMU3{3>0#cH)>-E7Fb4xL zE|i~fXJ!s`NKCviTy%@7TtBJv0o|VUVl}1~Xq$>`E*)f6MK}#<-u9w0g2uL2uH;F~ z;~5|aFmT)-w%2QFu6?3Cj|DS}7BVo&fGYwubm2pNG zfKnrxw>zt-xwPQgF7D3eTN17Zn8d$T!bPGbdqzU1VlKHm7aaN4sY`3%{(~59Mt>Kh zH~8zY;jeVo$CVOoIp;9%E7sP$0*Cqou8a-Ums!E502h{ZMVy|XH-E90W)USFDzSjp)b$rmB9eaA1>h zZ<`M7V|PcDSP0lL>GO^&xuaLpig7~Y3;E3E-f@>AOliK)rS6N?W!Ewu&$OpE$!k$O zaLmm(Mc^4B;87?dW}9o?nNiMKp`gG*vUHILV$rTk(~{yC4BJ4FL}qv4PKJ(FmZoN@ zf|$>xsToZq>tp$D45U%kZ{Yf>yDxT|1U6z|=Gd72{_2tfK_NV!wi$5$YHK zit#+!0%p>@;*o?ynW3w3DzmcaYj7$Ugi}A$>gcH+HY0MFwdtaa5#@JRdVzm>uSw|l3VvL-Xln~r6!H^zKLy zMW|W{Z090XJupzJv}xo0(X~6Sw%SEL44A8V}VDElH!d z>*G!)H*=2~OVBZp!LEl5RY8LHeZr1S@jirblOln1(L=0JXmj(B&(FeR9WkOlWteu+ z!X75~kC)10m8Pej+-&6T_*l|x`G(%!Dw)BrWM*0Hk-%zF{{H>1(kb7 z4)}@b!KeU2)@MzR_YE%3o4g*xJG?EcRK5kXSbz@E+m@qx9_R7a^9cb7fKr1-sL|Hx0;y;miqVzfm7z;p-)CAP(ZiJ zP1Y%M-_+4D9~cib;p}(HG??Wn1vnmg@v#rr&i#~r$Wwqk85%Axbzh6#3IZUMvhhU@ zBb%DLm(GHgt(!WkiH2z!-&2b)YU6_KW!G-9J9i_z)(0`howk{W+m9T>>TqI6;Kuqb z|3voT4@T;Gn&UNdx+g&bb`SsFzPp(G$EED)YUct=@1m(ZU8{F5ge^GUuf~;Y&sv=* ziv8_;Y3c?0@zpo_DU#(lUdOB1Khv)>OY90tw#Z*6m~Q(nw1v2@21||3i}LH~zg2&a zRK~&B2OrDXKnKp}GXpMm%ZJ^HTRWKRcroCL_|6xZoD-#3qpC`X$a{Y<{(DFR?P~WM zQQ@VwTnF!hBK3w(sjs%RMRvk>BDzO+c~_XeFvaf`)o;ylGq9&7%V_)#L?|%aFD2pF zoisAcCNS58Cjcq8wDKX22JiM0;_|1*TYpvgziQ-IT%qgY2JJ9>qg5V>?yDuVJdArVp_*M5f^p;!XL+`CZXIz z&rC=}cLo@_Z*DU{LE$PR$sXxXn1@wOg5yi(z4XV?=*+KPm8XtGOiM#Ju5zxQZ<-j- zWUgqFd9cs}49w<*_`4A`Bw*I&f|oI<xl5> zVFZ2Nj~iRjUXAa>(fXNh^l0ZvZCj}@-|mHBAfc{{giu1V*5YbZoWSQk4n50vJhk5U z(%~pjC}zxiC;H4m8q}m=m3wS(8#hGA^wk5xKEb6D;tiW=`Sq=s+BIa}|4PYKfRlyP zYrl_^WKrE&P?=hyvPG`OPl^JBy^IJP$fDS=kV$jySp_Zfo)VztEnxJtA5%{TMQ}>f z7)(c`oDc%)o70pZfU5mSJqy0NhtDg`JF1d_Q7)jK{(ULJE=`#LdopdJKEt#k4J7#7 zHOIUCTFM<46TmOC`1i`8O@L5bv&=_jYTiD>IYC~+Q+)RoebW3r;^Iehpng2|yd;de zJ5KgeWK#i0JHt%Vh8L}%06l3tR5^>%5BOp2+sz2Y<-MfS!PB1Q+#>y2%&eMwBd@3j z=bIn_S@vrd%|mYBFpKmmI7L9WK=$|y5pIxl8kb@Q#9?S5lzDIp^6t|E@mn5>h0@LX zK5t(Gk#`NN?T}O)dwhpjGXabPxSDo34&-s^4bs!=oG}g5WIH&+s$#qjWa}Qzc;|uF zjmT93Tt3wV$xyw$Q~~O)n_sRbDAq6)VeKQ<$BnQn+=~XDTd9hO;g~ILIS_U-iVNE> zP8T*%AbYt$AGdO!n3*5rLc@Me=!J(I1z=v0T1R`o5m|{)C|RTYTVNuTL!n>uc);VY zt1hK}GgHuUkg;EwmlnFSqOS2-CBtR8u0_ij`@xIE`~XqG)j!s3H>CR&{$1(jD0v2v z6LK_DWF351Q^EywA@pKn@mWuJI!C z9o+gLqgrVDv1G?Gbl2z+c>ZjT!aEb(B{_7@enEhJW20r8cE*WQ<|85nd`diS#GH21^>;;XS{9)Aw*KEZw0W{OW#6hHPovJN zjoem5<5LbVSqE%7SLA7TIMy;;N%3TEhr=W&^2TFRJUWPve86@7iEsH^$p;U=q`H!)9EwB9#Y=V-g&lcJVX;dw}$ zvE?Goc@I7bt>>~=%SafT(`sK|(8U+Z0hvZ`rKHT|)(H2{XAd;2_a?X5K#5EjWMF~@ z=Dx$iW|qOsStpJq`5mS6o{?&hDkjLH2Omg)(og-e>X->WQU8V^@vGI{=FC9ES5e{A zptfOTbCVipp$%$%4Z3!I{EpC`i1AM}X7`m)lAs2KXqp( zxS7r0jzS+aeOwl~0r4WDc$(~!?+=hpubxt&+pyJ|MT1$(WA>^N&d@0YIPh1RcUwrD zVClN;B7^C`fzofKtfG7=oGn!WXK-ng6(+_N?txi@qgah^A0zsqx??_U68mb73%o9x8I-BGbW3+qPbqD(RL3!8Is3{2QUr@pfV7s zyDvbLe)5av)u%m{PWT>milh>L)XBGX5hkYLbwus;=c-=K&e*&CVK0|4H9Is98XSS3 z?u#8@a~?u~@IWW~;+ve_(hA~~Fpp2>DDWKD-8{zTU8$j91k|r1fqwhasxVvo0@rBl8WY}*oQ9Qli~1-fda^B`uahETKe zW2a_^&5=2w7|N;ZY+Cn99syF%rJm`4_ehNznD=O)C3=B-MC=0}tSBRwzsf*r%ch2U z-|x@x9AkL*xT>L}=7IyUlfB$Wh-7}4GV?|UtBfPb|iP*S;^5@Xl4#xc-reL)N8g-aP-H;@?3A`?b4>#KAW#~2t$Lnf@L(h&flZE%(6UHif)My{j zHKntv_d94HiH`>MIeHL*46n>b$nl0U9XiixT2^=yst zTrW!v9UQnvt-ow8GyWB+Q3N?UjTr zT*VeybJ8~IEqwnvI1Z+8zpGbPQt*i4~_e?dK-4%6+$D>w61II;f zl=$T^9g&Htv*eRMTt2s^XOjYM37Mt}HRpl9vCaGZW`UOf$bn4W{Wlk*_=dx4?P?dG zc#bUGmYTaS^iXdm$hX@@-@0;Cv{8xFn0*_Crfn}XIG@HmE`rk z_0-#^aKI@cL52NhLEZr{LQq5cDvSB8q&3%qGa}t1t3Fhd+_iON`Re{;nlv=n^uo`( zn0&8)ZX$v7H0-r zBJE^dvRs$sS!1MWb2y{NIO<_huhf+KvH2^_pqq@=u{mwQM+P=4apqt>Mv*kd^v%AY z>FL~qxn5Hn>3~%y=6$CX)ZfvZt(a3}f&Gwj8@f*d?{BSvkKx-&1>jTwdR<0H-Q_{gH z(h+qS!JO~g9}y>>(0!#1RKpoU(;A+m|2df6OmoD#K6&xZXSO2=MeK49(A#1>_cSK$ zxNTS+{T1SB0)*+{nsumSHMf!pNG5HuA1`$-Wjg9T(L@gIMhp~B|Dm}cwL*0tGV+qSmExLEP?K_cA<;ea@WI{6 za6THY@lQURt`WtlVfNM*|8R28OSRM_Trp~14J z(Zzsnr9G0C2^O8T-yW7pSMI-|lgV2}v!)DmLWT+$y6?Y4yt8nJC?JpEDGwk0%`nH@ z{@YsI5Fkt(BdW!DT}M*)AT;Xn4EeZ=kmyOWLx}g_BT+b(c&wxKra^43UvaXoE8}*&NOlT4U)?L-3@=;fJx& zaGV?(r4A(EoRO!`4x5sfDGkfqDQ5ug=R+xpr=V3Gl<*vVyB4G9du)3ZA ziDzy}JA7@I6Kg;jB>IgnL+V`q%~d0KG(c5fuxODH9*a=M_KaVXzgA)8zi9;+J+nvo zkNl=-q^o~L;Z>owxJT@rd=E*8^!|~GduhQ|tU+9{BxPfkgdK6)-C#Ai*>ZbxCawR{ zL_C7c;xY(LU=X;;IMRj<#sis39%c`>|Le8OdCnNq)A- z6tK0J+l1)b(M9a<&B&1Z#Jth4%xQbdMk#d&1u)0q$nTKM5UWkt%8|YvW(#deR?fae z%)66!ej@HC_=ybH>NC04N(ylmN6wg;VonG`mD(Cfpl$nH3&z>*>n5|8ZU%gwZbU@T&zVNT;AD+*xcGGUnD4;S-eHESm;G=N^fJppiQ z*=j&7*2!U0RR2%QeBal1k5oO`4bW&xQ7V?}630?osIEr?H6d6IH03~d02>&$H&_7r z4Q{BAcwa1G-0`{`sLMgg!uey%s7i00r@+$*e80`XVtNz{`P<46o``|bzj$2@uFv^> z^X)jBG`(!J>8ts)&*9%&EHGXD2P($T^zUQQC2>s%`TdVaGA*jC2-(E&iB~C+?J7gs z$dS{OxS0@WXeDA3GkYF}T!d_dyr-kh=)tmt$V(_4leSc@rwBP=3K_|XBlxyP0_2MG zj5%u%`HKkj)byOt-9JNYA@&!xk@|2AMZ~dh`uKr0hP?>y z$Qt7a<%|=UfZJ3eRCIk7!mg|7FF(q`)VExGyLVLq)&(;SKIB48IrO5He9P!iTROJR zs0KTFhltr1o2(X2Nb3lM6bePKV`Cl;#iOxfEz5s$kDuNqz_n%XHd?BrBYo$RKW1*c z&9tu#UWeDd_C`?ASQyyaJ{KFv&i;>@n&fW5&Jmb7QYhSbLY>q9OAx+|>n0up zw2^SLO!XASLHCE4Im8)F`X1QNU}mk@ssu*!ViT@5Ep%hB2w0kS0XQbRx8B(|dSEMr zF^e0IZ1$x}$^kaa8ZGi}y=(Rn1V4}l?Tx`s=6Vr7^|9oYiiuHlWJ&7W$}3x}Agpk} zeM0Fa;wuFuzh&67?b5ElegEwyD4ctwO6z|2^Ryh;U^}gvl|f-s>9f9hL_ybM0@xG( zQ1I~tGO7&d2be|<#Cs(_l&dG8)_#H8s7G?8-|1Fi-ZN~Kf$1)`tnZ~?Ea2SPC~w!% zN5N}H_G0#jI!9Cw#D~!7Al;b%PS%DkYv#jUfx;B3nk6lv({hlhK8q$+H zSstPe5?7Eo_xBsM+SKCKh%IedpelOV3!4B6ur$i+c`Cnzb3;0t8j6jpL&VDTLWE9@ z3s=jP1Xh)8C?qKDfqDpf<<%O4BFG&7xVNe1sCq?yITF_X-6D6zE_o& zhBM=Z$ijRnhk*=f4 zCuo^l{2f@<$|23>um~C!xJQm%KW|oB|Bt#l3?A6&O@H=dslsfy@L^pVDV3D5x#PUp ze0|@LGO(FTb6f#UI7f!({D2mvw+ylGbk*;XB~C2dDKd3ufIC$IZ0%Uq%L`5wuGm}3 z#e?0n)bjvHRXGhAbPC)+GIh!(q=}cRwFBBwfc~BY4g-2{6rEbM-{m650qx z^|{n|;_zWeo2#3Y=>|Ve0(#Y)7Nywel&yjJMC1AS;p%g=3n+xHW&&@kHGo5uu=vKS z=`3?V6S|~7w%a5 z{}=htve$^OJZLo1W}!u*ZTG9|M}ecn)6-YdK>$e;PpbW+^8K8}!6N_KMOdDCdW!;} z?sFLI8mGJntXnvi29p;0^HLaV;t1fLNND@^-92U2w4$!I931qha#C`Q2sk*fIsVZS zBna`<`##i>ropjwol`Lv8)&Aq#+2uuqa5@y@ESIbAaU=4w-amDiy~LO&Kx2}oY0hb zGjdkEmn*sQy#_>m`Y<}^?qkeuXQ3nF5tT&bcWzljE#R0njPvCnS#j%!jZnsMu} zJi-)e37^AC zGZ9?eDy7|+gMy$=B#C61?=CHezhL$l(70~|4vj?)!gYJqN?=+!7E5lDP}AKdn9=du zhk#)cDB7uK#NIFXJDxce8?9sh?A$KeWNjKGjcPNdpGDHEU=>}`HxpYfgHfHh29cAa zUW2P@AB)UO>aKdfoIqg0SGRpc4E&-TfB3Y9Q%|WAj|mG4e1$IOk1CmNVl)I9Vm4wo z3(oVdo}JO$pk8E*ZwuuQ1THZ4-TXOKvqfwqg^A=8eE+D`MRVo|&eynm{Ofwwm}6xr zi-ZBSj>L9g$p$AoVv9fu6%h7%f%`)l+O2bZ@%rC3f+-_J_0ap(NLXgyPxdw$HM9~= zFABy^XplC%j6ExbJHBu#cganl#xs`^X-w*M1U9Y{Cs%L|!sU3)rK(498T1HYtO-*t zE>i}}Q^5VijVUo+a{N20QKeZ&mUB)$2x>!>nfd_<&42MzO_oU^Cuw3W1U>C8k4Z-;I)Hwz}clprW*1#cN9Eb zc+)>qHS%7}9^t&jOjsczIIrb)IhH|7_FvnJ#3iry6`pc8JS^|zdc`sIrW~1v44uAu z4cXW$3L?~kE9>1tR}nrfv_T83-xr!;EgYul%$1fy>9C%r0(M(5`Ww>Z8eY8jc)$22 z79&%(H(PfzKGg~3+n=o!mLRb+v51(qU9bb zgq44mOQDCxkf_0mCPe6MW31cl?In&&s*%%+%XbEe{59^Z=D4z^C9H>b{DB2~UamwF zuSv;}X)m89VM~{>c0?+jcoejZE9&8ah~|E{{pZCGFu4RXkTYB4C|2>y@e+&j`Bw8k-+O@%1cfIuz5?+=-ggCj*qoolI4MOO5YF&V{*r$zYEKQldnW$~DOE*= zjCNv~z^rJMo)l+4GaQ}uX*i+ZO3((%4R}J!+$z^OMmeQ@g}-0CU`Y!IT4V!T zsH%huM^)eDsvK%fc_5tS-u|u^DRCgx=wgz($x22;FrR=5B;OZXjMi_VDiYp}XUphZzWH>!3ft&F_FLqSF|@5jm9JvT11!n> z@CqC{a>@2;3KeP51s@~SKihE2k(Kjdwd01yXiR-}=DVK^@%#vBgGbQ|M-N^V9?bl; zYiRd$W5aSKGa8u$=O)v(V@!?6b~`0p<7X1Sjt{K}4ra2qvAR|bjSoFMkHzE!p!s|f zuR@#dF(OAp(es%Jcl5&UhHSs_C;X87mP(b;q0cEtzzDitS8l|V6*s)!#endR=$@lM z@zW@rnOyQ#L8v!Uy4Lf}gWp9dR=@Z^)2;d-9604An?7U4^zOHu-y$2d#C+DDwdwt6vZ)P1r zEmnfv)gMQ5Fez$I`O{_|`eoD#e|h-ho*m}aBCqU7kaYS2=ESiXipbeV2!9|DF0+)m zvFag{YuNeyhwZn-;5^V zSd2{0Oy(}~yTCmQzWXEMFy`G#&V>ypu4f&XDvubOHzbVle1bo;(7-=3fvAS1hB{r{ zK9-O65t+fFL#0b~r6L-?q<5=RcKTM}V$WkcEkv5iL&ukW?jO^a^rU=0Cen1H^wqC0 z{sv?taDA@di!}>PKt}4{dQt=zaJRlDSS3%YCQij$@El(EeS)@&@lx_+=r1t|Q3>2v zCDdxkooWqzrf(+dORYXyBnry^vm>wyd0hE~6T;p-9~f0^4m~AUeAv={cet7m*{2|~6vVAM=vpL?8r|>+7ZfuT;*FKMLJGNyc z)!M?FJlzd>mzyrCJi3SQM$eUS@xCJioofaUwqrzeQ%S|R`Aa6u$h3~pn3ge8H;U0% z+Z~w$tX*TF3?Bia(5OK1--uI#gzJ;b5uLoH{ZFw&E0w}REn0XA!4#HLjdvE}GHCBT zMj7g$9;PwAHTUKI5ZL0?jTRutws}W@-^ZQvY+I`RRUq^H(;hro2sF&qX0$Sn8yjq1 zS-XgbgdmyQukGKXhM9c#5rJ(q^!e2^A|dvfiB5oGPSLeAt5%D5*PeG3-*&*guZuuC zJBU$e7TQYCv=P5Uu*IQUHW?0y%33xDZpbd98PO};2E)HxOQVOU|UymxHgZ9B@5W$*}2MWJa*c^h+fpc9wwZ5c?$46XDvb@ z2}v~Q+LI9-eS9J4lf0KKW+gGo70QNXC1;t@eC1Od3WRDxuCWR+h{JeQTln@;u^A#0Ge4Qp1=`> zt(XIo8r+4#xfGhRFBQT(lgt$%8A30KhUoG{+ik~fuoeR8Ud~f*o zN#9})#5rW_+dgG!l}{1c%z{6AH(Tvg3|h;u2D`;{o73i$bqh7Iop3+H*fcNREDYT_ zV_$JL|Eylt9GKs|rOxX5$xtGCZEeAQKH}yQj-e(UJp}D!_2yJ@gWOA&MM>%1!demF z{DzSMQm{L!n=px(sn{+@2(U%8ziqH>-40JBY~3gL*LpzOteyy^!}jjLw(L1_o}Uk# zkKOf^Zc3kM+N-motfgs9@a}WnlbNk!W-goXTetqGjXAXc z$y3qKU$bLO7v=B~DBGp6MY8{jqh`(d-;*ilDsa5kLsG3nql?h0gTJ>LMhtReWbRU)S)mI$^JHKjp#>5BrWm#uS z&6^i@GHwk&nGLSz%FztTWa8``W>tAC{;-Vadc3icr+*5Tpg1 zb4{+jDC;o(mNXIT&m#g)lCPKSRP?zt$jhdxu=L}y*CL>gNCS=sCl`j~I9IwR0hkQC zNk0%Mc)XPszHT|{`-Hp9ZCH;eb4c<7?i;#qszYtx_-^5xDYJR3FZ*l<8yA}Xb}g`% zQvia(gm>;D3o7NQ-GgipuW{}`$MPFUGAzrbx{1i|?cuMGeLCu){I)gxeT2lY%p5>f$g;-r^p8fOaa7MlL zOB$w}<1+naU2bU$qq8(UphBVS{il1Y%H%Ot66gsPl;7oMV}Eif_WZ)$l#gYl_f z`!9^`Ih-`#inT$_!|E=KMw|AP$5OZan1c}{81&!%*f?-6`OBAih;H|eKf;SD7SvYJ zzI!=qL9#@V=6^Ed&Vox>nvRgDbxB_G?scQ-4ZOdqdj8RP9skm?jMwcFwCnt`DMh#3 zPx|w1K!Ml)Gcv<|7Q?Lj&cj$OXm*u%PCL^ivl`om5G&#SR#@4=SD~LX(^Jcxbdhw)5wf$X(QCS-?EVV-)KgU*f@rc_QJ!#&y zOnFUrTYr6Mk}Z@%Qbo3$IlJ$M@?-X_S_aKG-u<$&rk995uEm5|lZ&I?TEYt9$7B^P zh2HP!B7$3DdD#;0C|DAv-v(3*Q|JpR9rtw@KlcjR z0u>+jpcaF#*%yK3>on*QPT$n!hVmV?3Ts*6GgSv4WmL`R|5df<*oLdRtm2wssW!KC zANH}}tLuVDmi`i0E&R1Fka^c(-X?U*iL8Ni3u&xU@Cju*t3?-7mMgv#d@i~fK9iXzdGFDTymtyi!gn^Fzx1BNJP&lM zUsmCM#g|#v+_f=Bwx2VIz0a!?{k_u&wdY!H)n;5Filb}BC~Dd zleclQdsliFY_`v=OWBaLQw%{>Irf^2qsPwfC@p5@P%HZ<(=Xl}n2EvcWSC?(i?OY1 zvC~5z*DPj7bacJde*UiO7_88zd&53d@@}-WtQqfPE7fZ3pqKF*Fq#f{D`xfrsa@wU z<*UY85uCMZSrwZ8)Zjhj&4|Xa6JbcI39UBcTjM8SJm_RGI+SF6%`K{6%jaGz3>bn} z+_X**pz=y>rP<-ElPQyC5s&80wYvX>jrC9)DWiw(CWwmOALHdL;J%ZxDSOP~B6*A^ zvA9^=p}pk1%Hw;g2LAW=HZgN5 z)~zf0COD0!sIf(4tefY|r#UNQ3*Ed-xx_2&1=P{a1GYu(heIonxLsE;4z5%~5PV+G zn75(GucB<9ey_JzfqTF@|E^G{2lv&{W8A+uCNx8}!;{`fXXNVUWdk>vQT)x8#S=20 zxtV0no%fhw&@#V3{rh`fUu(DC;I3ADmQ?4kRO|GN3w_z?IEURYnw8c~?CjFGP#-#o z6gxi=DS(5ZOw^TRNj*Ya+u14%%PLH@XN&L{9qlq7QswNCL;D{qRJt{qk!YsZZMQQ& zpL9?2Be@!`V@xFODnG)ykGOt$GdusL$~Beo#G*t!R!z>WA%1S}UVPj`)8)QQEp)R? zNRlD9@_AzW1FNeC<#_Rnxwu`2rChms6a8n8-s5H)8!6wf;y=ezsBCb@2=?%+ZjD~>TkD?9{hd{mviZq&e@@syMi~U zd&=3NKjgbW%mK=%vv}3C|XwTn{657 zbb~Af2pBjxh4)hb_DyqU?}{vGa$0wA*G2sYHC$?DOmM^-6W#0b4l|R-yYDFkj_7%~ z4GR*+&k3YxnbR@Lwhi2Y$1K&)$0tR&(no+~FJ}E%z!Lfj33|sT#!5-MsBQ|fpxRI7c%fg$8dcKMWe0Kl% z5&ro-HQiOeU6N*GaPWJz@Xp;^$)vl2N`-Y+6Y>aJpuz5qRzjJ6dWpvbc+4+Vzlz!+ zMa$YdGf{^1e)cq$COm-0*!-aHVF}nYbz{GW)v>Gr)~Kp70Mb8(Y(ZihSi|qF5 z089q9BJI!Buu9C!yR2*Y2q4kcM{t?tq@|G|_%<@ea>STGXz2%?AASW~uXEq{Br=wk z;iYtbm+uz4>eazwD!eYWHz5TL$FioIQmm#<0q=S&yGv%>(jRr+j0xVP4fwW~TW!&C zW;FK}vhuHx>NIf;<_bI%=cHBC$gQaA$55KdxcRQYC}{A?n*LFZVSxOh>9RMUq!p+1 z3b+o2kA(^lme;OnzCpiD>d8gsM4FWk<_TASAE>{y?UnzI-kfutXG!&%xG*OQYE5*F zKRZ&$x^-pS>w0-i6XiYyMz`?ph1BT6l;^LoTMlfY1M1dsU~3NdWv|JT*W!B*rE?zN zL$=&u)^hz_W=Q*Hu=D)oB7Utxr|bE&BI={s8ij4!u?rlcer>!d<3W$RcL9~X;OWqh zSOiRkO`m12Srj~HGB&B)ExJ7|u50z<(mvj`L@%c-=D=^^l(TR?pzXQK52^Y;==qY< zbRwd8@ak?QQX2^_l?sygrJC<#-Opg|dNb$inQC298xt1{gp4!Wo&@1F_^@xEwSV(I0PKsI}kIF$b$=b-aygh z_b$B~T;22GMW4NvE`H-P(UguY{5O4^L-@Y)A^35c5x&<@_XlVuj^_#=jcOblZG9 zdFXYD{dweuA(en;gvv?Zj!k?tAC0ob&U7=9LnCI(7O$!wjHZbdX?2R^6+HWEZ%V9% zo*v1!(M=0%3%Va$Tnb&|yXAO!r=M81O3%#UKV2`L?dh#%H&0!C9C)}_jHl$DG`ufC zGqzclc(&4Bj`#B)7r?LJDesZEAF2vUhtdD~;y3HR z2K}eo-2b>8-t@0;kN*oyG18CF>1w{Y zBeHf{*q3<2*AtQf4s&-m0MsH$EBv51Nj=s=Appw|nd1Yi(-DKZBN$9bAlWN83A_)0 z$4U=S!XyBuAm(`t#aW=l*tHPgHRE~MrmzGWN*Eidc=$BV2uYe|Rpi@t-me&ht6I?| ze$M(9=%DxSVTwNL7B*O`z`fRE$T)18O{B^J5OHo#W%kD-}gAcJO3n1x6Q{X*TFh-d!yx?Z$G16f%*K?exQ+p ztyb%4*R_Y=)qQBLG-9hc_A|ub$th|8Sk1bi@fFe$DwUpU57nc*-z8<&dM#e3a2hB! z16wLhz7o)!MC8}$7Jv9c-X$w^Xr(M9+`Py)~O3rGmgbvjOzXjGl>h9lp*QEn%coj{`wU^_3U|=B`xxU;X3K1L?JT?0?+@K!|MWVr zmC=;rjX@CoW3kMZA^8ZAy52^R{+-YG!J5q^YP&$t9F`&J8*KzV4t3ZZZJ>~XP7}Bs z<}$a~2r_E?4rlN=(}RBkF~6rBo}Sz7#r{X49&!gODP+TcB*@uq57EII-_>qWEt44B z`5o+tysMLY*Dq^n@4_vzKRu3We5|DI+i%NV=Z|)QAl{di_@%07*qoM6N<$f(5Fv<^TWy literal 0 HcmV?d00001 diff --git a/mobile-app_legacy_rn_expo/assets/icon.png b/mobile-app_legacy_rn_expo/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b1526fc7b78680fd8d733dbc6113e1af695487 GIT binary patch literal 22380 zcma&NXFwBA)Gs`ngeqM?rCU%8AShC#M(H35F#)9rii(013!tDx|bcg~9p;sv(x$FOVKfIsreLf|7>hGMHJu^FJH{SV>t+=RyC;&j*-p&dS z00#Ms0m5kH$L?*gw<9Ww*BeXm9UqYx~jJ+1t_4 zJ1{Wx<45o0sR{IH8 zpmC-EeHbTu>$QEi`V0Qoq}8`?({Rz68cT=&7S_Iul9ZEM5bRQwBQDxnr>(iToF)+n z|JO^V$Ny90|8HRG;s3_y|EE!}{=bF6^uYgbVbpK_-xw{eD%t$*;YA)DTk&JD*qleJ z3TBmRf4+a|j^2&HXyGR4BQKdWw|n?BtvJ!KqCQ={aAW0QO*2B496##!#j&gBie2#! zJqxyG2zbFyOA35iJ|1mKYsk?1s;L@_PFX7rKfhZiQdNiEao^8KiD5~5!EgHUD82iG z2XpL^%96Md=;9x?U3$~srSaj;7MG>wT)P_wCb&+1hO4~8uflnL7sq6JejFX4?J(MR z(VPq?4ewa9^aaSgWBhg7Ud4T;BZ7{82adX7MF%W0zZ_mYu+wLYAP^lOQLYY@cUjE4 zBeFNA4tH1neDX`Q|J)mZ`?;#~XzBag&Di1NCjfbREm)XTezLrDtUcF|>r`6d+9;Z2K=0gYw6{= zO`r(C`LX~v_q!oQTzP=V(dpBYRX_m=XTYed%&nR+E%|WO3PI)^4uPRJk7kq+L(WmAOy(ux(#<@^3fSK25b1mHZ&DAw`q0&a5 zXU$pWf=NbJ*j}V$*`Y zMAz4Zi@A4?iMs{U8hRx*ihsZYHPTpP)TpG}jw4o_5!ny)yKkJoo=Bir+@d$gzUtPf z76rl^DOsUwy9uARy%q+*hrZZzh_{hGBXepC05GjPV+X0aCfbk@fQWuf;3wQF@_yMe zt5AXhdB6CNa}=s;{GA3bi9jK8Kx#cdW9+*ie&)lhyA|*h09Nk?0_r>m95{nVXO$6+ z$R>+ZL^ryBs*)RkM6AqpNS?#{nnq$qo^Vt5G+ytRnl4dc&s0sMr1WG4?WRPcp+ zP;4wHTl?f)^!Gj@FV%`g0(eGv;HbO<_}J0}FndK2L|Kcxs9q1mJ&rMg$cKcFmX!S! z0vJ1OH3owS*d>`!`*;8rrX8t`(L`=H!AifKdlcO~&e#f~Gz*D+&)!2#ud^j$6ZANS!q}@cvw*7N5+0Q4R zvKIiqx03&fsKF9NtB8=DY2R$GBF zFO>1hO8{sMa4qRW4rz_ZeDmKOIy>H_iVr#{5#Sj@pJ!sj&rhsFLFP!^^K&|Dr6uLtPu&2WmLoOp+72f`> zM88yjBZc@DHb&cF31E_s3Lc>O?h=~(jh!O*kcTy{W=1>28}m0z!NXv!+39S{1Oo=094 zX=(h?=(7}XGb1D8Le$|=j;d-;;crtG&kl~$1R;+jNJ~%pbCYscUVDFEU78K}k--e# za(QZW#pp2ud*;SAz*bwBzqqTRikI2Y#5?gmB4!gw{q?IKxBJ$Ekk*C1u@L4^va%|d zg`199czf=a{W_rZV(o9cO3-ss^nlj#!JCtP7Us%{K*#UAfC_J8t8O95*4X1neL!uT z7q+4#870U_4@PTELQHYcP!d#&(5s=1xX@nu4~{P ziXP#%91t7KLLnvdo!MHcGH5gCyUtMXC>j$4q!W8-qKL+{QA?W|P_g@&o};Qr{V>;Uw00_+`9LV$n}g$1Wz-iO^%O9@tw3qx-3ufU%wo0W1X6 zd5hj=!1>$2#x-W=@#r)rb>i#BX;&5+G{ip^1}TzYa#zzvid~=DT3juEZzPd*Ptx5PlmOekc^%T@qfGKnX zVLtTc?`|*HLs@&g^HLc-XM;hT*okFVoGV>Rk7|YR#rP|>d%?%Ac6a6tD?jV(PEM2| z)!GQ%0<#4uaBClL!}ieEL#lNYchYI!%yOx-k)Hrt@v}`10WkK6dpyGbIn3J}K<9>6 z&Qr3w#HH4O-)FlVQbmE0IsYU?*2#U}c**@5bJg+B;Z3a{C!Wn z%}5?fNU7QX-m!{(5YE8DV9$RRbxu+^pZ&ZnAiN>7Ej;=f|mchq~oo_duHA zm}UoOBhc=BYSg6-FC`~!vzKFuZxq)d%0s_mkb=8gcX@+)g%YXM+P;snBBP?OLzICI z^nONGyOXmz_6V@ewl4VaqES4q;1}i2cE%ze0*luwQ@4j=-woV5=th~qD7<$}vxHqH zki`K3_K?tAp3?w8qw7CdG)(7lggoq>PPlkt@rNqVm`Ycg!CT9)9T8abyZIZA;Y;5m z%X*dax+I%)X7Yjc(a(`}0da228T?%A)(62CEkfr13$PzqKi>>_-(@aRUSr2JRNn||G!L%}1dKJ|E9+0HUy|x0-9#8- z__=}bb&@;)o<6PQ+SsWesX{>caBlo2%~rhkUU6n+Pfy5N$X8vK18kZm*^~XJsG(og zBO`Kur%3CE5}R|r$by?(@1|{;bLg+dG6WvJ5JO>#SNDdi)Mq0e&KQ?o%pyICN1`}n zIPG++itoD%6Zjho*jBp)LaVIDkPL41VQx_s+y{K#ZZMFUJN!!59D>C?pv3!jpgav( zrWmF`%6QG9&{*|Y2TOEg;yXX+f+FH}@zJ?z;cQ;60`OsF+Pun!-_^Oh_aQkQeRK|! z@R;}3_d5Uqj>@W;{SAaq0{e2oR($}c?m}x>mw3U&EK8p zbDNT;)(io|2H)fID;xYi(7M`Pl2^igo1pxecivhQoZrDJYYqKXg7)kPm6M}H&wk?1 z|CR)0PYBK27ml4L*mD4!ulgjD!q2H)&b>^b(Z}^4enh{P^oa<(*DW{p)=!K!Cf2yxArAy8esW_t$!wO}OC;g>-Y;p?(8K5Lqzo zVOhL8FZn_oA~?Q9?Wp}%Z1Q|bKd}2%!+#WJCx^^$C*0K6QZ2#Lm}2_VciwAguz0^a zyw?EN>H_b-HZ}3A`6@(yG~8IYa)emU9NjV=esnMsEpL5I0ZtmYfC8%y6>s_lxxw#E zG^q&>1%X%Rq$(&YCp2v6OnGR-mI-$;?ekV}$>8saMk6~@idK;{+s(Zq?`iUsro#Rn zzK=vUonDa1DE+ob8@-xJ^13dF>)CrThqq%v97t^q4e`&PYde{8V33VaZdX`=oBAPu4=@9clN{P5AM&b z`|?IsKKKQs>6f)XqgFHWEv{GF=(s$!WorDO7lh60_n?q_z;I`mZq z*dn<86V%zQ*m>k6jwwD*+Tvl&G&c*s)!Qmq5P(FqOG?8SR457Mh3XI}o* zNHJnfNc3rddr4S%F5TL`3ttEi2p&B*92mBV{y_fFcD~9Cc1oH&eyi!@W)XDmr!-Lc}2ziivlJ7K)m%-)5hd*#%qjqpv-I0wp)Ww;Zmhe}i%+uMaYSzlf15j7cS4Lcg zSw_~_f!|o?!98lFa72N~m5HV*@680?k@kjT&o_ld&VK=i#LoRgmXTJI{t}u-HdRZ?xP84*Y8~` zqFW_yBG2VbRtq|$md@m7E{$t7b^3%Cqa|@prg-_BqkTptrIu-ROancLO)(0 z`=1nJO?$p%(=%NhuS`x@r3G||Oy!YPtYHd3F8}Gpd5? zgBlTI*{@j)(&e2)r%evo5bP~_(UYOO{MQk^fQqpvQIEd=s`Y7!rEyHF6#dd&lqXBj z{|hLWB%YCqcVlq&AE8P_$lodI-p~4@dR;nHMQ2FmIOOL`<)D1t5VfCd_YzcanOlBt zsL8m#o5134a;vzx!oLHR`N~~sP@WwvT?bz)a<^pV!b6r$f9^=S!iu>(V~l$UF_QW@ z!jio9i1}8uto)xGyTH-HFBncUqGi4lrD{Q`&u+;dL z7?|h3?1oggBM*H{DI5sULUT1H*YkzV_qLG^sc%iIgZTIw;OSOeyh1tMAY zSE>_9do_gknQA?7{grd7)rmnvoMHyAhTAnruXGW5CH(TqWX~?>l+3`Z`IZ{MAO_}t z>z0mi4wXAv4ZRp4DOLP=OH9o7w>!9tx#eDG2oy4Ma3!FI|DH(Z`MZqlPjidSN?!+$ zxAP0oI8On(1j=wbLHW9&CxWKM7y*dfaz2%0e>3Bk9$HH+poGt8IM4O2Zp!L+{o>)TGM-lB`>PR8Dne1b=v{V}GsGFDR6 zL?jl3X>eP9=IXDRx^qg$yDfIGM{KhS@4j*WHp6TdG>Mie2RHg82( z!YwvpPJtaPNlyo|V5-ByJ~FNdS3jtrR5LFZZFjc~l%lkvldKPru(A4oET?;Mo0KeZZgt?p`a4@) z)CnT%?S_k4DegHCHilm~^F_lg&w*-=5wnY--|%|j;2c`kM4F~{#!A9F)TLy9i5Om! zGf^3|Fd`_!fUwfTJ2E~!Q?Nf4IKX|HVM;0LSu(H^|202t;=Pkd%$wl(mvzH4!mEbw zygM6z8hzkanzrS;p+34V;Ahu&2H1nB;i!W~D1yw={CxUbmC`pccY_aa!KB#G3x?Ji zjkKo#t+c@lLa%4C|1#`FT!RHCmzUmffD-n|KTh5?_aJ_j@Nf4G@ZKA5hRyL~KE=D;$L6#A z+anClym(vFCUa6`mh2H+eCQ}j7N2II_7beG;%^FrtEsL|yur#E`@#U~)2`~Y^efsA z&Upac9Y>`9d312?bE^)0sxhayO07&;g z#&4bUh`Z(-7Y*$M_{0jbRs9@D@;s;4AI~j|qj`T1G9)vhRn0lBf&; zDThp@IKRj>^IItes}_6lK!YanIoN&LGLU&fXeWbwO$Lw+3`D`~?+tZ)+C3D*F4VD! z!YA~jLKQc(iUKMbQ${@@%PvI=Cvet*TcTe`3Tm9?Jw8D`#1kU0%T!+yTD58D#$S?< z08SIHoPJ5$Fu7)8-82N`9ssG(k|}5@(`$kkOa^DI=sjZ>mJDIzT@2*l#~G!|Y;P30 zEuj{><|Y7e0`>g8mDh}S)d-(egD^KCCcoEcx=L42Y*7{IQPA_2Gj63jC*yH7VYxse z^WgiuLu--n2w?CMkhX~&mpdQ?WAV5g_oGDJALfosHq;QF2`+9#-&$?d77|K|-T`aV z+KtI?WJ6w|m{mH^#phJS02_?+l7+Op8`d)%&%CXKh)>}rVP{1RNQ;v^0vU&c_mg}) z=~Xr1v*?=v8`h%Z(4W5)bGiKujAq3i}g-nmv90otzcnAI&?}v10NoRzG$vHYtyd4DyePWNt^4l%sO^^H!E(f~f8VWd6 zaJO8ZJ&I;+fTqUsn|B1gu%75Zzq_eGBQ(ZuR)Zt@d4&PdgiG-=F~!N8!zgM0#=p=> z+GPqp`i^As;$u*G^A&%^ML+kf0E*Dj;~-lx&ovlnsXlm+u4shDPz!rV$sP&RKi|8G z|6ruV{hm;FVq8i|l0F6a1wYu8{yckALq*+Y>?Xe)`jeFxXP#11gM(6xUBeSk{Uk!krUo5_7H>e;Dv&W$_2jrFH?#*z2jY zI#JyAOQ@r-f0EX@5RWJ8!L|#5xZB3zS2t_qd=bafdoDfGk8lF3pL8KAZ!a4!!pgf83>i5Pu zYMyimE!m+Pmb_Cldje-6xU_|0Y~>W12^QzJUQ%KCfn-h(j9E~e3Rza5+0iCjw=GkR zllb*}Z;86cW~@;2#H$^c?SJjen|Sl%_P;(afLk#HkXSF6^#|7u~~%Oy-b&-M3mB zF)Nw4XIen0`tv16 zUQginofO=-m#!+HAyx5_)7k><*g@oL(=yTyqlA8~)>yHvh1y^rUuUl|# zX@i}tPv7iUsqQXZG$9MxrNW8?H{CBD{?0gIv|}eNLWrI3|6z_KZp)J8kIAx3`nI`v zt!LS*vFdaj6)Dg7@H4xJox2zl%!i(imn*s>~@mV%AwKd#8KUFwB& zsSP3wcW}%>|F!f^RigSket-v+*WKx%61S80a{Wkv_#Epof`lZKNR<`w^~r~xkgQ$3|sxDc|{U&nVydhl3 z5zEN}oJ`pV{udB9#Pgu;WrF(!CAP~yte|3PJ3KnMU4zxuhn{w+$U_6zeNK0}-V(8T zgBs86T&@CVG+5dDki6y_0YK$NCZ?s>68}OCmdv1jjBwgApk%Vl5O&WmNnmUbPR9p= z8=TL5VlG1b?Z8?9uY5Fb#-(Ca&__o^EzC02_O!n$pmUEcluV)@_mE8G_r7g{ z_dMXFp3`5VcBcz&2MP)FotYrnziA%ADhbT`;&Ak?>a(iE$j4wQ3*>1=%u=6@W^d-C z%A0mJAG1qSL9I{~*5uT(0rwc&$7OB58ZO&-S@Fq*eJO+;gL|V0+B|VwE|{mlwy&vl zgIqxW`{S9=(Z_^TBe@wDxibSgU!NH4kui-Vtf02zv`cDBj-yuqg+sEjCj|C`%bCEz zd=kBf@b^zG#QC+Y^taq&f>5r6Jz;_Y0JF+M#7-rxfdn~+_XuFj7@zDz7Y!k6LSo$4 z$wm>j>f*QauR^_q@}2~WpSig8*rvl1v^_a%eD5pXhgbDkB`mompqC=tJ=rz?(E=S*zcha14B;fw`=0=Vl# zgMX@BccXu%)OHr^5;@K=bbFX5Nwh7X0Gt`DcnnM4LDq?(HMn}+Yi>c!UV>MgD~62( zz*Zgf$8KU|VoDT#%^svR|3%G4!?Vu%0#YboHfZpIV5L%~V?g6=gDp91Zq2Vt2(x1M z77X|ci>WCA|J04*{}gkXhJ5ILR$)pUeJ3mhMt&Xtgx`FX(a=dzs9rdk8u90I*_@`_ zth12y2|+N)Lf?KMI)~=XJBIe%q~Mol^c#HbRX7E4PlS>4x)3$T;RmP;F(BMKK*SE5 z{)0t5YoK5m;t(td&e9&^*&9*FyHA05x1VDD!sk8c5ktSwKpC`#vG$jPAetb*=iBy$ z>&Mp?mGMJs`6l^9tOa09&^^SVUc7i}h&4SyPuUxD)YFkzn1md*nE@dxAxDv_bBOk# zXqA9%{Ai@0-zGeif6w7I41QxK3U;xSpq=7%(x1Iq)vdNoU}xemV0yJ zp7HDQfyym#9qDVe6<{;O0bJ|9IPfYkoIxYRY=XToDSunStmuT3fFT64FNWDKgmGvD z+f6=CH$a|_tey)ajUTUAI=(O7+LKn>f5AQEF3Bh7e8pbYAwz~5egE7&ptm+z-r ztWoekP40Rl7K4-YzWjX{be8rm34X7}$`P2iORL~tixDmlq;Z(fG2o+6@qWrhOStVH zbFcjxChq=9_whhS;w4xF7=1W?>Tc(uzAY@zJVX0>TUFAI4CAZ({12O=K;08G;HA}m zTle>T!oaprs}9KTCixt#IrR`=L^qo~CFr$2!*6|hf=&oCk!lpxnBpJVeO(9`3TWUz zZDza?g3o_-DtI#na}{pxV%bgz{6@2-t|V?A&nt_S1jF1s{BopN-!rP?!q3KJq+J4X zTV>T0fuo^!)nIXJJRwXu#an<$St-rAHVvxLg<$z_;7-Ff&?=hkh+PKb3LYhn3(357 zDnQd1arx>TLs}B3|G?tC_R!SP-r zw?k?T@6*IVnPNzb5UjxT#9LtWdM#V~D+v|Cun;5jN}Nb=>u(MG@@Zs%8>2HGlbMu= z`%Pbj7}DG~>bwy~&0C>?Y z=Ebap803V9nrSLWlB0m#wf^lDz8jeR{RNkf3n(pvhmRn~{$~@9B*CW6Lj1A~xEO;^ z=ahG9j{u)sV1->1D{F1bm&T)d}DZNCGRjEBpw}K1i|b z#T=G>O^6Zw1^7m}Pk2$Y>SfknQS)zt2RC1|i)j${u&nn!|=9;ZYe-{Wb@? zRyg;gyZDsCD0rCvVZ-dYSgc(1$yY?0eT+#-*^ln+xfo+$?4hj+6b{e`mEB*rvx2qX z9?~=^hk9F~>6E?ocXN-Dq-h~r8RbqKX;HY|qIb9lTy|SyZ-7#NpBFz*TM_5lQf9M) z);F*BGk}$qK~up`>nKwFp)PWhrXcOSCYx=j@i-CFkcVdP^uHo)A%YWvm0DE2@HETU zHjUOU(KtnAaHMlwCX7(*v>3IOVPEjZz+L0v-eQCA(6r8gK#Kn9L7Wid&nszI!9PyL ziTfR#&;G2Z3Zix}9E2Ea>R=iYV2mF=G#icUe)U+t1`aNHMD&N(-zKfu5JKNrNWA;; zD(VPWTDdrNo)%%s&&My{$^xWo@;@X(z~dLj8Os#?z~^thrTkOw1PN9%E_P5O4h!NO zBy@|K!p=CRg$#G8$@PhaK*yFm_P-3?xkYFr>*QZc%4{)AGZ8l~^-N}&7=a{dk3!~)!n3yks4(~nhE0wleQu)VTDwl*>Uk^-2Gj4kQ*l>vLAU^j$%7@IaFaE8@0 z3+dWFd@ab3WmUHBX`ruH0!@0wF-_tc5a;j6>m8^&Or>Ib!PR}jU`GZs@`(21VCOIA z1ghU0)IsLDEE=pCSw!gou?-)uI-XmTlYlMum7H#9be#y@S9Yzkk7BU1QZ-%oZLqu2 zECe!NhNpcOm#t+zq#vxuop!(byd(5p^ORt-5ZJlP1>6k*rca9CEfu}`N%b_KCXTuN z_29!yXf20wQyU?cgyCEp%v3?v;9+k1&6qSv(3%$MwtE7O0!w`&QQ*PpCwIn>7ZS7# zqrh~jK--svvT)WJUVaF=}_FZ?L%^AOmN)&-7wBK+d>6 z)}kj_AS$2c9{zGy7*e%GJ_O?{zo2PRrvuWC>0Ol<1q1TH*1chmD!BE<9YRz`@BHBS zC<7RUL#|q%;MW1K$EC-?^h5=Afdb$jVoc9$sw3x@;iCh7avo={xt8I<^m+8XJ3Rpc z|D)s#sNWp|b2q9miZm(EN)T9H-0LLVVLF)G?2qf2mgP5 zk-yAxE#$J{9`irn&WLLP7>oYxSiDE=r<*xqd{b<*Fac1#h^}mZLF8?uaH737@S)5? z>|mi?h-%CRaDIZJFNLvadCv0#^=JqF&qvu4;^Jl*1aV~Jo<(d+q__;9qV=NkHIeB?H;{gu+oLz=pX zF;2vEjY=KRwZD8^Xl(r~SzZKg;hQ$cIk@4V5FJ&&zppbTVfzX9W#IGh;0|*zK6*!T zpVtA%`BBB#-4E*KKz^cZ@Q>y?V0rq7`|W^xl7JRr_8JNy#b168_X^}&7`uVG7m!-X zdqs0_z<-QbrW>Sh4pgq;$FeqW%R@7GuT2Eyv{V>ix=B6Fo&UDQ?G)10{SqOk<@&ww zX6~c2M}^&27F2e${pMltA2fUS84aKHJ6b;o;l3fQfxDO}0!`y{;y|`@ zMTJNy5u`k)Jyip@30b2^MBYS?0Q!P}Bzzmo)_12HaLg}2QauF+2MAk;99YN{Y*83D zZahhIpNPMe5iAJ*A^%!QcNS!$eawnb>8GD$z475a`<4D(qVqsAhyq`Jm7GSi2e+gP zoZZev?JNDqcq!I818$!c$n3&bY-&{xy#T=$>z@r@MpxX}15`o8%Q|ypRnc)yFg`zb zWW9EwA~ib=3R(hopPP_E}og1_mqyHwHqH`>JPK(jK3U+6qr%&EDiuevSEe=wQ=GH}5$N zo5U^;$A2(Hjg;Ki>2wE64xb{|(=K}k8qidag5Dlwhd&hyXk}1ytqnh8&9D)IgPgLM zZHrDnH3OjQm6zS3?Zh0@@93aZ@)S0>Wig43rR{-;;{qcu8eeNA*Pr0F3cT5#IZnE+T~Z>)gy+e_Q$xsj*}TIUz5Bd`7LREo`%zq zT9a88Gs%pwD{P1JIx3n|(r#^f$4|RK_8Ja7pofd^UT5hx9?4Lcgqv^T1$bM=^(We+mGxRi6*8Ipg z;PPw#RQki84bK<0I4w3#gH}D9pW|>1Y>?KhgQ5}|dTv?B9?TlQ^z{75CZFW=<_Yvs zGzfXrCXku~zp?>6_-L`L7Z<{vOv|UCkkYAr0b!rE;4MoA*gG^lK92~tQjF1&*Oq}) z5O0s2K8c4+EkT9>vbF9wwN4eh)z|SKM6=1!$Q^MvGy4c_-0VYPY8~lndlVQk$)e#u z?PQF3bx!BCZ4XWU21kp&^m1HC91tf@k#0SOtg-t9I-lXi-_<;~kJgJixU?RcU;8{7 z@)M2QFejGga0u$h0H0T1rng*P(&Y3{_=a5$ObI8(ZBCE`vD|cn`e&;Jht7I*#T7|V zr$|2v6jZ_1FXA7C81?46k^SBW&w|+^m}^XK;1l1dnS;HitpLUEC5yk7|D#1rm?Z) zg&P;AwTWL*f&ga;qusIEptBAyKKyDj)tEeHpILiMNAGN~6M%P(ZqiPZ2TEH&*-F!f z6~&;}Uz=BW9o6<(jv3^1t+b8E#)LeuErSpReL2(q{cq`vD+;`nG0LaBK*5{QAOcH7 zUKNFR$i479)BYRD_P7*|@&*MrBmhP*pNl6+GX^A1J$kv%>K_n~mjpa$ofX^|jMZ-x zhR+JM$3>Lp3}V1pVdP;Va@ykoNZwLOZg<<7ySZ~ zVrYV0HZ*9ithjz<&v}cP%0$YlV{98R;>_9Cy*(vQ+gCL;J14v1to%<+flFbW0%vbr zo_5p^37EI{dMt4zhH^la(|_;q+!WozZ17sauRU;7a943PDIaP@9w4n&uzcHB$~xZKw$x)E5L>JU$XZtC-K6W9ZQDGil8&(C<^w!V^)6 zNC_}mvjVLH9Ej=bB?$Izl%q`^GT~`|;*Ev9ne1t|>bP;Q`32zS)~`B*DaAd}^>p=r zROYm=E;Q+1XXAUOsrQpBX5Bdcgt3vE5&ZF}asB)Am#G@)dB6Onv9Ob)O@Q-!^zy19 zXa&8d*mDufmCoK zQy(&#k4XGEc*e3Ap5veCHM{#fs}c={uAEz<>Xt!6JVNRrI_sm?-_};^HMAzv6he zzJ7i;H0!YLc4>+P0rtQQE>!bWxL0|w* zjxBAUBj&B>tGyH@JR$r^n(7VekMfOhLK|84th-9kf1JC`pRBJ&vco>0PeDG!zJz`u z4g++no(Q2fpf`%q&7jW%54KY{k>Dut(#ugdbN|U5xZRe70mzQorRg=HWk=iP6OC2qnOWDytmOau8PU9a$_gVr!b=s}mk=^LHAN zhF;wBXZf99rLWu{1tLWK$^{Ew0%_h$OlF}r5pW*?0=>w5=W92XjG73Bx}Be3oxeg} zRkV&?DhK1y_5}Js8x}cRmtea@uSF8NA;9!K&?+9b;T|F2CvT+4zo+z06rq8?KEZbQ zddUG7i`dQ5F_|wO(+GzARU`@HENgRmDL>A3f%H>CqT=hTS}Lzn-y1p4DH8?G_2|n! zpyv`|xDlg^BDgt-#MQfDS^3@q)5L{wFvaoEgIBJUkdiqAA;GdN?`xxt4~$)CyLcOB zi4}vO>Sy34#@Y*Sz6#40mRhLg%XSVt`cNQ>e2GI3hb6?=QN5+4K zpC%y`n~>&je;bM?WJtOA#1L5lFI&=Khe{AEABsK~@kXuHA=Lh1?k3tU=o&mvuTjm9 zmWMOfLn>OF(#pFlN*D2DRB z$7c_YE;}Qfn)l!J)Sp}{oohJ8q%C9~j|7^m-6v$I1rfU{#h2C-EY=eCpqSfEG=0h| z5%I1`VOP1+(tk(ACyD!%`X*7_&=2{&-%RPrK#rp=_TH4T5_1u{p?FcOYIX| zbam;>yyqKFzaTY@vvKH7%3fMd5>K7Hf1!``V7EA{ z1wfp4Pd!A;Kstvm^z=AAQ1*5zEXWGy2d^#@?rfFeY!((vGw` zDdT0qa^$BC;Gifg9Q@PvUrwx3;fP1DOkGH%a>_$x80qX}tQ$WJ zqe865Jb3J)%JpLfw}t%onQ4aI-(#IaXaw4%-Wj zXg>WbwKSV@FpBojDzRtfkBig2*_t*vo=bXyIR~e^$P103Eb$Pt+CW70YAj z2_gq57u5l3KlPY-`|l|}%PI9MSgD17lw4kCb?wW*&EhW0PM;6Dra9|#Q?C66l>%!g0MA-f46xZaAU@`@OSeBho_TBL&2DXRGdheZ~P(Z)}XJq2Q8k=q8N$` zL;S>jYc@wOBwOe}X9xwDqor4g`L{f4FEpuYgH?i0pUe6+hH{yNRtR=G1QX0kgH)dn z-gA@VWM%~2QX#znU+mL*T@=@v&B{d8La-YDWGrFV{t}w*l#8 z-8?eqS=B}mIRCXGtM~Uh!7C6jhqjwxd3qg;jmUmql_zVIzej$q|KOQuKS>LH_iO>! z0=pZ|T^wbx>dF+n`hh?MX4H4-%n6Zd9&9?WSBt>!g`QqQ> z+xI;;rbR0~ZERT1-|?FBAjj(P10exmQ)oM>6!UAl{(@=qiKoHbC&7ivr-yQmUkmmq z%*fv%Z@LqtC7oz^dYMobXqf)7$XW+1xInOVZtBl#^8-~= z&Y|KAqijRzdGE0*3-K*(A{E+KDC1$wAXVdylLr{zT1oub<7J-e1dW{R*oeDV#2M96 z&Iu%*@Z@Tm1%nTu&fH&(7Hl&(jI-qP51t$R}hJ{Z~{i+tbob)(Tr zZUAZs`y{LrcqY&RJoxQPTcft01g4pIz>Hn=OMxH&BKtqJsb<0&ZX&FPl<>jE7jDQ` zpwnujjafn{#H)fL!|FiApOcyY0DC+;zXOrekddL+Z~89FHeTykiP?athQ^tIZ3HoJ z2ULxy4orq4KEHK>-fM_YX*k~^%3nJbL2GECl6s7~5y(Q5ZK?wOnaIe^2~P*qtV6(V z1&;i}eS%2vHI@k<53C8*k%dEYdE^TZif;Jdy&Wb`4-~M5ix!&n4z6IDcJ zvt)%^3k3MK4AmT7z0dE|qTaldwnj6~l3bq-X|iAr?+Gu)^;NSbN0cIUg}S)0*AMg2 zYHjzT)5WyI1XJkYZR)zqDw8UAz4cu9Xg6dU*%CZ~>20c>Y~yD?^oI6%+u?H0VQKwA zy70#FuKY0~`-2uy2}&cD%wE4^Nj_-p zRhJ9BP%vMZUr*6p(T!7A}v3+URVm6+e?B9Q7i3|P)NaorWDmpz;PX(cJ> zs_kx9aqq|7+_0P{a^$`{LjE+~%>$i7SV^j45KN^Oxx&G&d5Tqp3mdp8MIUUmPa#(x59Rm$?~Jh*N`sHcsBBY~3YF4KF(k=0&)Ao=sG$!j6loq>WMrvGo4pt_ zV+)DWC?5$$VGxOIX;8w5!OZXR{eJ)bet&<>eeQXm<(@P5dA;s)&pB~b@8zq=k*{~c zo+b+Tevv7!NP6JD%7%AOs(V&|IPxsbt&!1pqdFp^TlK813HicpPm>MQ1F2%`LqB1r zzNi_M+VX?0=`=z^S*pU!&kUPN*naNY3BNQddunqPbsf1*bSt5Ur49S@8~<@K;caS! zHf8q++8mVo(EDf>o7!x-Y=sqzJiJt?>}v5#mla&JBMMYaHoB~asR6bYlOuN|h_R?? z&O~~^GZtRqs-nh?^O)Svt-~4TMhQ)eH04F?>z{1MB*r~YAlrxgsR139W;MNnuJAJ} zco#7P;jt*eaxQ)MQRs6ewODwL61f4@{Sh;Pg$_0)K>T@%p{wYHhgV&3IPNn>*Agog zd>k^bhS)T5mawZ}@B?Vuf=ntXvUs-&^Q8F2z7?DyEG9!rF5v(<8raq`BRp9wtK}

_m_Cz!aI|OA~=>rPyDZB}LviY`DTRyq;E+O1bb*mtHP+eDp`ie;@gD)I~c+6GFbPa%hM z`8Vex*~}cS+digqY0sJMuZM`)j&b;BN&8Bf8ycw7yWTmLRzF2`&mV!i;_!0GY1hGp zb*$&h%G&BIe^cNQG&UZZL;uTN8%^xvNkkx~^#*AkS2X%ziIv8gqo$-Nk*@_^rPWH^ z*L)RAHm5TNw>h1~z)`GS!g!lHyu<>rZ>9iOrAIRH!X2`(0Nu~%Lxif$TC5$#DE+cE z{ijLX5#>7=*o}4n?U~M}J*BAU9vkM+h)#@@4!X98>sImyC=SSCNgT*sNI%C2T>i<-!9=`VB~MoE;PLJfXms7b`3UkFsopktZsUu2`1dq zLkKAkxB;K`WB#D)vXr>P;vI^hlReihTzq^o^ujke-_P4>d&|7Z>G0neSdVpD=_A{p zzaXC1y}rJtmP2<8MZ2q_YZJL9G7Oh;K{yL5V|e}*m1NTIb3GA>WrghgOgWuW{3aYU zC!vPfD%{X@ANAJ&0p;vM@vCuDDUKM~vORWNZI%l6eB+aw;A5p(Le52ja>c7Dso?Z& zwJa(*Ju3oD?8P4uRoM4M$N_2sO2~Y$I{|HGih=XE!=%b(>#B&zHELo519p)LB}gf- zIcriktD7O1*bNvLRB?xUzAHNJL=zjS55!G$oTK{=ZsKKXWsUA>L407$9?hfeuNv~+ zV(7Nu1QQsdH@enfB8Y2~QO~5;=if?cz*gq9X|3Oj_Vr;ouRHdF_LpwG7$hWA?kw3I z7lNtHprmKTT;3k$nlzOWd^!OqefbPJs~VbLtR(+^r?&D;fs8LVlbz?b9l`FSq~E(Q z91@`=0oM3ougBzcJV0l?;+o3fAH7d^yD$I5@`-MzfvacD@$=fV=KQoICRXSms6$j*@>%B4$Zu&2iJZcpZYc6IalE1 zvefh96Nz{OLsVyVDL-r{ysURGx|WF#U5f9I>~y(I5`<}kCXXnY+n?H0FP$I_-U7NC zxGwSeTidqo))zxLP)@I5(L~*=60Ol$Z|zvxKIIeB@$eRugHua)KcSQG)z^+&6VTUW zGtS?*TVEaJklp@53!^@M0ri?zw*fJk58rQwXay8SlYr?8f8V)T5>yKz;CSB*aYb_tKPX(}k z<-Nmh>UaB*isssB>l(Sc?2X_1yb(&R{dv+c%5t+gBCN;0xu5V?nJWM1H61Xu#Q*ew zJ3g<6)$zcaK4}DZ6IW4tG;oOLZ6<<;6p{b;!^tC7(Ks^) z7)I|ml)Sf?8KO4675nLqP{t$9E@ObSbK$D%tRu=_g_8-a-qXAKb8gT2ENXawopM}4 z0`lHRiIa78$mX9-^xSbw7iByhx3cEk`BBmpZkY%zy)f+zaG@Bq(IQtnzo z%PE_dB+x4QTfAxUhdM?2aBnQt7!^jLP z6p1kMLr{zdHvBSSTdkwCAXC?&5(J9{m-Ddn%kR(4`PhTobU%IrLb8Xe#eG)?%W0Dz zCiC}6s*q#m0+iHJhxXXVNrcM6jX(nHy~;=~xk4PSZ&~V2j?k zG|`DtuOZxpw-AY`^ORuoHM0{}8K&Q|>4z}_GxXGN26MhH(*yL)Wh#Wq)~aU7Y+-t> z2Gi$X&&c{>T-F`5Id&^R_U(!2wJTKOCLLzNOV-BSUQ;j8Q_q&Bo)TCfrbifrN`A(C zsH8<9&qKAN7yoI|fj4+LZmmiVQ< zr)G;VNGNJ!3WxTKPt)_?T-;#uwgw5u2GX}-upj0;v5T$T^D>^-KKl#8xUn$h*i zDKNN+<#-{d5?`yhYH`5sJC$>we$z~cVgB&3Jlr7Xs@bI=O}lU<@hcjBqsqiK(ddWR zYH?T;6}Jl8x@9lZ+iv&Fx08o7jo19{-!6WPLCH=sPP5mqNwP(Pe7Qa@-c*=m-8&6YljhO=0g=sdnhY>(3u~b(HH7@hHN! zX_EN{NMW6@`eU4I(!C1BI za8t+(oEN(5)x_I2Q%qwX2%Ga>6go|O}1S`eIgR_1yGQ?Hs-gyHadT(a8-+F!f z*)M+!Jx-xzC>i(}?yZ@6l485#m1y7R-Cf2u5bj1IZk^rTLEjINCq>OKTR9g$^`6)* zr9)BhS$FoZ(+d&QTZ~+`h&Q(?vO6>Il=h8HlDRsrr0>_6OD&&gzv9_NO);lzCZ8Y; zlZw$=iRH{7R#O9Q@WEj$xOA^PfS3a>_!E8cF;wGL;mDCQ%|Kc%DHEo5d}1cD zd9eexRBf?fEF`B65$6Z>3Q1koOhDvF+{lM&T=_X1q^7>_Ff1P>l?AE0dR;LShNmC~ z_@Lr)p+XNXZDGu8g})2-Jq7hry0Tg?gDg&N^$nqJ7WBcLE6LH~-@}7>Bc25)q;?>m zMU(z~brJ_7V&6_d4=G+9NFt`doaw#pgaxaojM?Vx*@f62rL3DlsW{2CULK+K7og#3 z1tLqeluZc3rCJ1e?U}8P`xKTNeNolv3Z6F}{ zWeYeL>MG~?E&R4;0^cr$Wc|YG3@A#FrgaMsbmdV3bC}}Q$P@fl-zo{zxaBwS_AGkq zh5l*L+f{%=A@|J)p&zkGt#s9UIpjVFDi)!dk;Gv~FMr2WL}E7gO}COZB2n_I*t8Vj zl~Mg2vDV1*ulDL2MLtTP;{;dY(}*G>GCZIrt_Zmyhg|i$2r3A~uuAfsFH-hIvE{d} zc&&Z<1O~v)g+GgFvnx*d-7o$FX$$q;LtkiWyAcAxOL(F+0K0mr3qK5xu1vhe6A`Oh zD&31jfrychVu37ZscaUNdFcD86P-1XR;NfIWx=OV`q2?e8sy4sa ziLnwCyu#GvqAVK?w-V@l#EA~_=;_r!jb%*J<7SdkL`W(*(1!n*aYYNEX`-zxnAW;g zhsNcRs*9+1v@LRq1^c$V_{VPNgOIc8l@vbTdXU{|a9}xQ z1j!X9x2p_NmI=RgC}3bMC1@tid=-wnJef4(FMPWecsB5oaJ{RH9t&D)2u;^xYC4c! zOu*McDTa5XGpeG+iAFZEzz~t|lmcC1?pc^bM7XP#}O^uD@>2uHf zvY@iHgUC7+G!Du~M)<3e(0 zz6vYN92GBHwcKV=9C*E+{BCQE!>Re>8P6m`yiMT;GrqX;4=+9h6yc zcumctv&^SaUv@5ZWTN5r5yLX|cceP_gdt@WSE43Q*656Q>d?GpFTo^s~$(q0a!#*Y0^2DTl?R*d#Ly|?u@6<(g3mi!=$zFfeZ zv$uR~_T9qh?LQfRk0swkGBA@x#u}lsAu@vCyW-uelR1ZORH@y28R591A;ewXIxt!- z_FpjlQ$LCN$&0}W;@x1HmiZlhx=-}H6*1C2chKjlM95CX;y){Eyu&5Z>s*@AdtFn} zMCi$NlTn?0W0GAd;urGp;xO|Wuc2pVNKR;WDXOE<9|bSvf7CX(sp4EETTrb1oEpmc zOBM`^2Jlm_*`+>i5_+U#G2wpt&gMBQ%x5<8GlS+u`vrGAU*YlzaodXC-kWq0>q@_f zn5zMiqn8{>*#AD@W0DC>26`cvj{oli-hCX6>?l5MjfMU*;QyH$gE0WW`&~tyL1z_C z#zZrwk#?@a+?*z)mFq$h9WQcp93kMDOGtxP5rgsMKfnJI^lzee!T$^Tfk^zHAfD*o eYX2uFQ^E?}>e@W{JrCL6z=m|hvgm+s%>M!WQ(8m- literal 0 HcmV?d00001 diff --git a/mobile-app_legacy_rn_expo/assets/splash-icon.png b/mobile-app_legacy_rn_expo/assets/splash-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03d6f6b6c6727954aec1d8206222769afd178d8d GIT binary patch literal 17547 zcmdVCc|4Ti*EoFcS?yF*_R&TYQOH(|sBGDq8KR;jni6eN$=oWm(;}%b6=4u1OB+)v zB_hpO3nh}szBBXQ)A#%Q-rw_nzR&Y~e}BB6&-?oL%*=hAbDeXpbDis4=UmHu*424~ ztdxor0La?g*}4M|u%85wz++!_Wz7$(_79;y-?M_2<8zbyZcLtE#X^ zL3MTA-+%1K|9ZqQu|lk*{_p=k%CXN{4CmuV><2~!1O20lm{dc<*Dqh%K7Vd(Zf>oq zsr&S)uA$)zpWj$jh0&@1^r>DTXsWAgZftC+umAFwk(g9L-5UhHwEawUMxdV5=IdKl9436TVl;2HG#c;&s>?qV=bZ<1G1 zGL92vWDII5F@*Q-Rgk(*nG6_q=^VO{)x0`lqq2GV~}@c!>8{Rh%N*#!Md zcK;8gf67wupJn>jNdIgNpZR|v@cIA03H<+(hK<+%dm4_({I~3;yCGk?+3uu{%&A)1 zP|cr?lT925PwRQ?kWkw`F7W*U9t!16S{OM(7PR?fkti+?J% z7t5SDGUlQrKxkX1{4X56^_wp&@p8D-UXyDn@OD!Neu1W6OE-Vp{U<+)W!P+q)zBy! z&z(NXdS(=_xBLY;#F~pon__oo^`e~z#+CbFrzoXRPOG}Nty51XiyX4#FXgyB7C9~+ zJiO_tZs0udqi(V&y>k5{-ZTz-4E1}^yLQcB{usz{%pqgzyG_r0V|yEqf`yyE$R)>* z+xu$G;G<(8ht7;~bBj=7#?I_I?L-p;lKU*@(E{93EbN=5lI zX1!nDlH@P$yx*N#<(=LojPrW6v$gn-{GG3wk1pnq240wq5w>zCpFLjjwyA1~#p9s< zV0B3aDPIliFkyvKZ0Pr2ab|n2-P{-d_~EU+tk(nym16NQ;7R?l}n==EP3XY7;&ok_M4wThw?=Qb2&IL0r zAa_W>q=IjB4!et=pWgJ$Km!5ZBoQtIu~QNcr*ea<2{!itWk|z~7Ga6;9*2=I4YnbG zXDOh~y{+b6-rN^!E?Uh7sMCeE(5b1)Y(vJ0(V|%Z+1|iAGa9U(W5Rfp-YkJ(==~F8 z4dcXe@<^=?_*UUyUlDslpO&B{T2&hdymLe-{x%w1HDxa-ER)DU(0C~@xT99v@;sM5 zGC{%ts)QA+J6*tjnmJk)fQ!Nba|zIrKJO8|%N$KG2&Z6-?Es7|UyjD6boZ~$L!fQ} z_!fV(nQ7VdVwNoANg?ob{)7Fg<`+;01YGn1eNfb_nJKrB;sLya(vT;Nm|DnCjoyTV zWG0|g2d3~Oy-D$e|w|reqyJ}4Ynk#J`ZSh$+7UESh|JJ z%E?JpXj^*PmAp-4rX?`Bh%1?y4R$^fg7A^LDl2zEqz@KfoRz*)d-&3ME4z3RecXF( z&VAj}EL`d22JTP~{^a_c`^!!rO9~#1rN``Vtu@^d~$&2DJ0 zI`*LVx=i7T@zn{|Ae&_LKU;BmoKcvu!U;XNLm?- z`9$AWwdIi*vT?H2j1QmM_$p!dZjaBkMBW#Pu*SPs+x=rj-rsZX*Uwl!jw##am$Sla z={ixqgTqq43kA2TwznpSACvKQ?_e*>7MqBphDh`@kC8vNX-atL-E9HOfm@-rwJ=!w zDy4O~H&p86Sz}lqM%YCejH?s7llrpn7o|E(7AL-qjJvf?n&W*AizC+tjmNU*K603| zOZctr603w>uzzZk8S@TPdM+BTjUhn)Om0Fx>)e6c&g69aMU3{3>0#cH)>-E7Fb4xL zE|i~fXJ!s`NKCviTy%@7TtBJv0o|VUVl}1~Xq$>`E*)f6MK}#<-u9w0g2uL2uH;F~ z;~5|aFmT)-w%2QFu6?3Cj|DS}7BVo&fGYwubm2pNG zfKnrxw>zt-xwPQgF7D3eTN17Zn8d$T!bPGbdqzU1VlKHm7aaN4sY`3%{(~59Mt>Kh zH~8zY;jeVo$CVOoIp;9%E7sP$0*Cqou8a-Ums!E502h{ZMVy|XH-E90W)USFDzSjp)b$rmB9eaA1>h zZ<`M7V|PcDSP0lL>GO^&xuaLpig7~Y3;E3E-f@>AOliK)rS6N?W!Ewu&$OpE$!k$O zaLmm(Mc^4B;87?dW}9o?nNiMKp`gG*vUHILV$rTk(~{yC4BJ4FL}qv4PKJ(FmZoN@ zf|$>xsToZq>tp$D45U%kZ{Yf>yDxT|1U6z|=Gd72{_2tfK_NV!wi$5$YHK zit#+!0%p>@;*o?ynW3w3DzmcaYj7$Ugi}A$>gcH+HY0MFwdtaa5#@JRdVzm>uSw|l3VvL-Xln~r6!H^zKLy zMW|W{Z090XJupzJv}xo0(X~6Sw%SEL44A8V}VDElH!d z>*G!)H*=2~OVBZp!LEl5RY8LHeZr1S@jirblOln1(L=0JXmj(B&(FeR9WkOlWteu+ z!X75~kC)10m8Pej+-&6T_*l|x`G(%!Dw)BrWM*0Hk-%zF{{H>1(kb7 z4)}@b!KeU2)@MzR_YE%3o4g*xJG?EcRK5kXSbz@E+m@qx9_R7a^9cb7fKr1-sL|Hx0;y;miqVzfm7z;p-)CAP(ZiJ zP1Y%M-_+4D9~cib;p}(HG??Wn1vnmg@v#rr&i#~r$Wwqk85%Axbzh6#3IZUMvhhU@ zBb%DLm(GHgt(!WkiH2z!-&2b)YU6_KW!G-9J9i_z)(0`howk{W+m9T>>TqI6;Kuqb z|3voT4@T;Gn&UNdx+g&bb`SsFzPp(G$EED)YUct=@1m(ZU8{F5ge^GUuf~;Y&sv=* ziv8_;Y3c?0@zpo_DU#(lUdOB1Khv)>OY90tw#Z*6m~Q(nw1v2@21||3i}LH~zg2&a zRK~&B2OrDXKnKp}GXpMm%ZJ^HTRWKRcroCL_|6xZoD-#3qpC`X$a{Y<{(DFR?P~WM zQQ@VwTnF!hBK3w(sjs%RMRvk>BDzO+c~_XeFvaf`)o;ylGq9&7%V_)#L?|%aFD2pF zoisAcCNS58Cjcq8wDKX22JiM0;_|1*TYpvgziQ-IT%qgY2JJ9>qg5V>?yDuVJdArVp_*M5f^p;!XL+`CZXIz z&rC=}cLo@_Z*DU{LE$PR$sXxXn1@wOg5yi(z4XV?=*+KPm8XtGOiM#Ju5zxQZ<-j- zWUgqFd9cs}49w<*_`4A`Bw*I&f|oI<xl5> zVFZ2Nj~iRjUXAa>(fXNh^l0ZvZCj}@-|mHBAfc{{giu1V*5YbZoWSQk4n50vJhk5U z(%~pjC}zxiC;H4m8q}m=m3wS(8#hGA^wk5xKEb6D;tiW=`Sq=s+BIa}|4PYKfRlyP zYrl_^WKrE&P?=hyvPG`OPl^JBy^IJP$fDS=kV$jySp_Zfo)VztEnxJtA5%{TMQ}>f z7)(c`oDc%)o70pZfU5mSJqy0NhtDg`JF1d_Q7)jK{(ULJE=`#LdopdJKEt#k4J7#7 zHOIUCTFM<46TmOC`1i`8O@L5bv&=_jYTiD>IYC~+Q+)RoebW3r;^Iehpng2|yd;de zJ5KgeWK#i0JHt%Vh8L}%06l3tR5^>%5BOp2+sz2Y<-MfS!PB1Q+#>y2%&eMwBd@3j z=bIn_S@vrd%|mYBFpKmmI7L9WK=$|y5pIxl8kb@Q#9?S5lzDIp^6t|E@mn5>h0@LX zK5t(Gk#`NN?T}O)dwhpjGXabPxSDo34&-s^4bs!=oG}g5WIH&+s$#qjWa}Qzc;|uF zjmT93Tt3wV$xyw$Q~~O)n_sRbDAq6)VeKQ<$BnQn+=~XDTd9hO;g~ILIS_U-iVNE> zP8T*%AbYt$AGdO!n3*5rLc@Me=!J(I1z=v0T1R`o5m|{)C|RTYTVNuTL!n>uc);VY zt1hK}GgHuUkg;EwmlnFSqOS2-CBtR8u0_ij`@xIE`~XqG)j!s3H>CR&{$1(jD0v2v z6LK_DWF351Q^EywA@pKn@mWuJI!C z9o+gLqgrVDv1G?Gbl2z+c>ZjT!aEb(B{_7@enEhJW20r8cE*WQ<|85nd`diS#GH21^>;;XS{9)Aw*KEZw0W{OW#6hHPovJN zjoem5<5LbVSqE%7SLA7TIMy;;N%3TEhr=W&^2TFRJUWPve86@7iEsH^$p;U=q`H!)9EwB9#Y=V-g&lcJVX;dw}$ zvE?Goc@I7bt>>~=%SafT(`sK|(8U+Z0hvZ`rKHT|)(H2{XAd;2_a?X5K#5EjWMF~@ z=Dx$iW|qOsStpJq`5mS6o{?&hDkjLH2Omg)(og-e>X->WQU8V^@vGI{=FC9ES5e{A zptfOTbCVipp$%$%4Z3!I{EpC`i1AM}X7`m)lAs2KXqp( zxS7r0jzS+aeOwl~0r4WDc$(~!?+=hpubxt&+pyJ|MT1$(WA>^N&d@0YIPh1RcUwrD zVClN;B7^C`fzofKtfG7=oGn!WXK-ng6(+_N?txi@qgah^A0zsqx??_U68mb73%o9x8I-BGbW3+qPbqD(RL3!8Is3{2QUr@pfV7s zyDvbLe)5av)u%m{PWT>milh>L)XBGX5hkYLbwus;=c-=K&e*&CVK0|4H9Is98XSS3 z?u#8@a~?u~@IWW~;+ve_(hA~~Fpp2>DDWKD-8{zTU8$j91k|r1fqwhasxVvo0@rBl8WY}*oQ9Qli~1-fda^B`uahETKe zW2a_^&5=2w7|N;ZY+Cn99syF%rJm`4_ehNznD=O)C3=B-MC=0}tSBRwzsf*r%ch2U z-|x@x9AkL*xT>L}=7IyUlfB$Wh-7}4GV?|UtBfPb|iP*S;^5@Xl4#xc-reL)N8g-aP-H;@?3A`?b4>#KAW#~2t$Lnf@L(h&flZE%(6UHif)My{j zHKntv_d94HiH`>MIeHL*46n>b$nl0U9XiixT2^=yst zTrW!v9UQnvt-ow8GyWB+Q3N?UjTr zT*VeybJ8~IEqwnvI1Z+8zpGbPQt*i4~_e?dK-4%6+$D>w61II;f zl=$T^9g&Htv*eRMTt2s^XOjYM37Mt}HRpl9vCaGZW`UOf$bn4W{Wlk*_=dx4?P?dG zc#bUGmYTaS^iXdm$hX@@-@0;Cv{8xFn0*_Crfn}XIG@HmE`rk z_0-#^aKI@cL52NhLEZr{LQq5cDvSB8q&3%qGa}t1t3Fhd+_iON`Re{;nlv=n^uo`( zn0&8)ZX$v7H0-r zBJE^dvRs$sS!1MWb2y{NIO<_huhf+KvH2^_pqq@=u{mwQM+P=4apqt>Mv*kd^v%AY z>FL~qxn5Hn>3~%y=6$CX)ZfvZt(a3}f&Gwj8@f*d?{BSvkKx-&1>jTwdR<0H-Q_{gH z(h+qS!JO~g9}y>>(0!#1RKpoU(;A+m|2df6OmoD#K6&xZXSO2=MeK49(A#1>_cSK$ zxNTS+{T1SB0)*+{nsumSHMf!pNG5HuA1`$-Wjg9T(L@gIMhp~B|Dm}cwL*0tGV+qSmExLEP?K_cA<;ea@WI{6 za6THY@lQURt`WtlVfNM*|8R28OSRM_Trp~14J z(Zzsnr9G0C2^O8T-yW7pSMI-|lgV2}v!)DmLWT+$y6?Y4yt8nJC?JpEDGwk0%`nH@ z{@YsI5Fkt(BdW!DT}M*)AT;Xn4EeZ=kmyOWLx}g_BT+b(c&wxKra^43UvaXoE8}*&NOlT4U)?L-3@=;fJx& zaGV?(r4A(EoRO!`4x5sfDGkfqDQ5ug=R+xpr=V3Gl<*vVyB4G9du)3ZA ziDzy}JA7@I6Kg;jB>IgnL+V`q%~d0KG(c5fuxODH9*a=M_KaVXzgA)8zi9;+J+nvo zkNl=-q^o~L;Z>owxJT@rd=E*8^!|~GduhQ|tU+9{BxPfkgdK6)-C#Ai*>ZbxCawR{ zL_C7c;xY(LU=X;;IMRj<#sis39%c`>|Le8OdCnNq)A- z6tK0J+l1)b(M9a<&B&1Z#Jth4%xQbdMk#d&1u)0q$nTKM5UWkt%8|YvW(#deR?fae z%)66!ej@HC_=ybH>NC04N(ylmN6wg;VonG`mD(Cfpl$nH3&z>*>n5|8ZU%gwZbU@T&zVNT;AD+*xcGGUnD4;S-eHESm;G=N^fJppiQ z*=j&7*2!U0RR2%QeBal1k5oO`4bW&xQ7V?}630?osIEr?H6d6IH03~d02>&$H&_7r z4Q{BAcwa1G-0`{`sLMgg!uey%s7i00r@+$*e80`XVtNz{`P<46o``|bzj$2@uFv^> z^X)jBG`(!J>8ts)&*9%&EHGXD2P($T^zUQQC2>s%`TdVaGA*jC2-(E&iB~C+?J7gs z$dS{OxS0@WXeDA3GkYF}T!d_dyr-kh=)tmt$V(_4leSc@rwBP=3K_|XBlxyP0_2MG zj5%u%`HKkj)byOt-9JNYA@&!xk@|2AMZ~dh`uKr0hP?>y z$Qt7a<%|=UfZJ3eRCIk7!mg|7FF(q`)VExGyLVLq)&(;SKIB48IrO5He9P!iTROJR zs0KTFhltr1o2(X2Nb3lM6bePKV`Cl;#iOxfEz5s$kDuNqz_n%XHd?BrBYo$RKW1*c z&9tu#UWeDd_C`?ASQyyaJ{KFv&i;>@n&fW5&Jmb7QYhSbLY>q9OAx+|>n0up zw2^SLO!XASLHCE4Im8)F`X1QNU}mk@ssu*!ViT@5Ep%hB2w0kS0XQbRx8B(|dSEMr zF^e0IZ1$x}$^kaa8ZGi}y=(Rn1V4}l?Tx`s=6Vr7^|9oYiiuHlWJ&7W$}3x}Agpk} zeM0Fa;wuFuzh&67?b5ElegEwyD4ctwO6z|2^Ryh;U^}gvl|f-s>9f9hL_ybM0@xG( zQ1I~tGO7&d2be|<#Cs(_l&dG8)_#H8s7G?8-|1Fi-ZN~Kf$1)`tnZ~?Ea2SPC~w!% zN5N}H_G0#jI!9Cw#D~!7Al;b%PS%DkYv#jUfx;B3nk6lv({hlhK8q$+H zSstPe5?7Eo_xBsM+SKCKh%IedpelOV3!4B6ur$i+c`Cnzb3;0t8j6jpL&VDTLWE9@ z3s=jP1Xh)8C?qKDfqDpf<<%O4BFG&7xVNe1sCq?yITF_X-6D6zE_o& zhBM=Z$ijRnhk*=f4 zCuo^l{2f@<$|23>um~C!xJQm%KW|oB|Bt#l3?A6&O@H=dslsfy@L^pVDV3D5x#PUp ze0|@LGO(FTb6f#UI7f!({D2mvw+ylGbk*;XB~C2dDKd3ufIC$IZ0%Uq%L`5wuGm}3 z#e?0n)bjvHRXGhAbPC)+GIh!(q=}cRwFBBwfc~BY4g-2{6rEbM-{m650qx z^|{n|;_zWeo2#3Y=>|Ve0(#Y)7Nywel&yjJMC1AS;p%g=3n+xHW&&@kHGo5uu=vKS z=`3?V6S|~7w%a5 z{}=htve$^OJZLo1W}!u*ZTG9|M}ecn)6-YdK>$e;PpbW+^8K8}!6N_KMOdDCdW!;} z?sFLI8mGJntXnvi29p;0^HLaV;t1fLNND@^-92U2w4$!I931qha#C`Q2sk*fIsVZS zBna`<`##i>ropjwol`Lv8)&Aq#+2uuqa5@y@ESIbAaU=4w-amDiy~LO&Kx2}oY0hb zGjdkEmn*sQy#_>m`Y<}^?qkeuXQ3nF5tT&bcWzljE#R0njPvCnS#j%!jZnsMu} zJi-)e37^AC zGZ9?eDy7|+gMy$=B#C61?=CHezhL$l(70~|4vj?)!gYJqN?=+!7E5lDP}AKdn9=du zhk#)cDB7uK#NIFXJDxce8?9sh?A$KeWNjKGjcPNdpGDHEU=>}`HxpYfgHfHh29cAa zUW2P@AB)UO>aKdfoIqg0SGRpc4E&-TfB3Y9Q%|WAj|mG4e1$IOk1CmNVl)I9Vm4wo z3(oVdo}JO$pk8E*ZwuuQ1THZ4-TXOKvqfwqg^A=8eE+D`MRVo|&eynm{Ofwwm}6xr zi-ZBSj>L9g$p$AoVv9fu6%h7%f%`)l+O2bZ@%rC3f+-_J_0ap(NLXgyPxdw$HM9~= zFABy^XplC%j6ExbJHBu#cganl#xs`^X-w*M1U9Y{Cs%L|!sU3)rK(498T1HYtO-*t zE>i}}Q^5VijVUo+a{N20QKeZ&mUB)$2x>!>nfd_<&42MzO_oU^Cuw3W1U>C8k4Z-;I)Hwz}clprW*1#cN9Eb zc+)>qHS%7}9^t&jOjsczIIrb)IhH|7_FvnJ#3iry6`pc8JS^|zdc`sIrW~1v44uAu z4cXW$3L?~kE9>1tR}nrfv_T83-xr!;EgYul%$1fy>9C%r0(M(5`Ww>Z8eY8jc)$22 z79&%(H(PfzKGg~3+n=o!mLRb+v51(qU9bb zgq44mOQDCxkf_0mCPe6MW31cl?In&&s*%%+%XbEe{59^Z=D4z^C9H>b{DB2~UamwF zuSv;}X)m89VM~{>c0?+jcoejZE9&8ah~|E{{pZCGFu4RXkTYB4C|2>y@e+&j`Bw8k-+O@%1cfIuz5?+=-ggCj*qoolI4MOO5YF&V{*r$zYEKQldnW$~DOE*= zjCNv~z^rJMo)l+4GaQ}uX*i+ZO3((%4R}J!+$z^OMmeQ@g}-0CU`Y!IT4V!T zsH%huM^)eDsvK%fc_5tS-u|u^DRCgx=wgz($x22;FrR=5B;OZXjMi_VDiYp}XUphZzWH>!3ft&F_FLqSF|@5jm9JvT11!n> z@CqC{a>@2;3KeP51s@~SKihE2k(Kjdwd01yXiR-}=DVK^@%#vBgGbQ|M-N^V9?bl; zYiRd$W5aSKGa8u$=O)v(V@!?6b~`0p<7X1Sjt{K}4ra2qvAR|bjSoFMkHzE!p!s|f zuR@#dF(OAp(es%Jcl5&UhHSs_C;X87mP(b;q0cEtzzDitS8l|V6*s)!#endR=$@lM z@zW@rnOyQ#L8v!Uy4Lf}gWp9dR=@Z^)2;d-9604An?7U4^zOHu-y$2d#C+DDwdwt6vZ)P1r zEmnfv)gMQ5Fez$I`O{_|`eoD#e|h-ho*m}aBCqU7kaYS2=ESiXipbeV2!9|DF0+)m zvFag{YuNeyhwZn-;5^V zSd2{0Oy(}~yTCmQzWXEMFy`G#&V>ypu4f&XDvubOHzbVle1bo;(7-=3fvAS1hB{r{ zK9-O65t+fFL#0b~r6L-?q<5=RcKTM}V$WkcEkv5iL&ukW?jO^a^rU=0Cen1H^wqC0 z{sv?taDA@di!}>PKt}4{dQt=zaJRlDSS3%YCQij$@El(EeS)@&@lx_+=r1t|Q3>2v zCDdxkooWqzrf(+dORYXyBnry^vm>wyd0hE~6T;p-9~f0^4m~AUeAv={cet7m*{2|~6vVAM=vpL?8r|>+7ZfuT;*FKMLJGNyc z)!M?FJlzd>mzyrCJi3SQM$eUS@xCJioofaUwqrzeQ%S|R`Aa6u$h3~pn3ge8H;U0% z+Z~w$tX*TF3?Bia(5OK1--uI#gzJ;b5uLoH{ZFw&E0w}REn0XA!4#HLjdvE}GHCBT zMj7g$9;PwAHTUKI5ZL0?jTRutws}W@-^ZQvY+I`RRUq^H(;hro2sF&qX0$Sn8yjq1 zS-XgbgdmyQukGKXhM9c#5rJ(q^!e2^A|dvfiB5oGPSLeAt5%D5*PeG3-*&*guZuuC zJBU$e7TQYCv=P5Uu*IQUHW?0y%33xDZpbd98PO};2E)HxOQVOU|UymxHgZ9B@5W$*}2MWJa*c^h+fpc9wwZ5c?$46XDvb@ z2}v~Q+LI9-eS9J4lf0KKW+gGo70QNXC1;t@eC1Od3WRDxuCWR+h{JeQTln@;u^A#0Ge4Qp1=`> zt(XIo8r+4#xfGhRFBQT(lgt$%8A30KhUoG{+ik~fuoeR8Ud~f*o zN#9})#5rW_+dgG!l}{1c%z{6AH(Tvg3|h;u2D`;{o73i$bqh7Iop3+H*fcNREDYT_ zV_$JL|Eylt9GKs|rOxX5$xtGCZEeAQKH}yQj-e(UJp}D!_2yJ@gWOA&MM>%1!demF z{DzSMQm{L!n=px(sn{+@2(U%8ziqH>-40JBY~3gL*LpzOteyy^!}jjLw(L1_o}Uk# zkKOf^Zc3kM+N-motfgs9@a}WnlbNk!W-goXTetqGjXAXc z$y3qKU$bLO7v=B~DBGp6MY8{jqh`(d-;*ilDsa5kLsG3nql?h0gTJ>LMhtReWbRU)S)mI$^JHKjp#>5BrWm#uS z&6^i@GHwk&nGLSz%FztTWa8``W>tAC{;-Vadc3icr+*5Tpg1 zb4{+jDC;o(mNXIT&m#g)lCPKSRP?zt$jhdxu=L}y*CL>gNCS=sCl`j~I9IwR0hkQC zNk0%Mc)XPszHT|{`-Hp9ZCH;eb4c<7?i;#qszYtx_-^5xDYJR3FZ*l<8yA}Xb}g`% zQvia(gm>;D3o7NQ-GgipuW{}`$MPFUGAzrbx{1i|?cuMGeLCu){I)gxeT2lY%p5>f$g;-r^p8fOaa7MlL zOB$w}<1+naU2bU$qq8(UphBVS{il1Y%H%Ot66gsPl;7oMV}Eif_WZ)$l#gYl_f z`!9^`Ih-`#inT$_!|E=KMw|AP$5OZan1c}{81&!%*f?-6`OBAih;H|eKf;SD7SvYJ zzI!=qL9#@V=6^Ed&Vox>nvRgDbxB_G?scQ-4ZOdqdj8RP9skm?jMwcFwCnt`DMh#3 zPx|w1K!Ml)Gcv<|7Q?Lj&cj$OXm*u%PCL^ivl`om5G&#SR#@4=SD~LX(^Jcxbdhw)5wf$X(QCS-?EVV-)KgU*f@rc_QJ!#&y zOnFUrTYr6Mk}Z@%Qbo3$IlJ$M@?-X_S_aKG-u<$&rk995uEm5|lZ&I?TEYt9$7B^P zh2HP!B7$3DdD#;0C|DAv-v(3*Q|JpR9rtw@KlcjR z0u>+jpcaF#*%yK3>on*QPT$n!hVmV?3Ts*6GgSv4WmL`R|5df<*oLdRtm2wssW!KC zANH}}tLuVDmi`i0E&R1Fka^c(-X?U*iL8Ni3u&xU@Cju*t3?-7mMgv#d@i~fK9iXzdGFDTymtyi!gn^Fzx1BNJP&lM zUsmCM#g|#v+_f=Bwx2VIz0a!?{k_u&wdY!H)n;5Filb}BC~Dd zleclQdsliFY_`v=OWBaLQw%{>Irf^2qsPwfC@p5@P%HZ<(=Xl}n2EvcWSC?(i?OY1 zvC~5z*DPj7bacJde*UiO7_88zd&53d@@}-WtQqfPE7fZ3pqKF*Fq#f{D`xfrsa@wU z<*UY85uCMZSrwZ8)Zjhj&4|Xa6JbcI39UBcTjM8SJm_RGI+SF6%`K{6%jaGz3>bn} z+_X**pz=y>rP<-ElPQyC5s&80wYvX>jrC9)DWiw(CWwmOALHdL;J%ZxDSOP~B6*A^ zvA9^=p}pk1%Hw;g2LAW=HZgN5 z)~zf0COD0!sIf(4tefY|r#UNQ3*Ed-xx_2&1=P{a1GYu(heIonxLsE;4z5%~5PV+G zn75(GucB<9ey_JzfqTF@|E^G{2lv&{W8A+uCNx8}!;{`fXXNVUWdk>vQT)x8#S=20 zxtV0no%fhw&@#V3{rh`fUu(DC;I3ADmQ?4kRO|GN3w_z?IEURYnw8c~?CjFGP#-#o z6gxi=DS(5ZOw^TRNj*Ya+u14%%PLH@XN&L{9qlq7QswNCL;D{qRJt{qk!YsZZMQQ& zpL9?2Be@!`V@xFODnG)ykGOt$GdusL$~Beo#G*t!R!z>WA%1S}UVPj`)8)QQEp)R? zNRlD9@_AzW1FNeC<#_Rnxwu`2rChms6a8n8-s5H)8!6wf;y=ezsBCb@2=?%+ZjD~>TkD?9{hd{mviZq&e@@syMi~U zd&=3NKjgbW%mK=%vv}3C|XwTn{657 zbb~Af2pBjxh4)hb_DyqU?}{vGa$0wA*G2sYHC$?DOmM^-6W#0b4l|R-yYDFkj_7%~ z4GR*+&k3YxnbR@Lwhi2Y$1K&)$0tR&(no+~FJ}E%z!Lfj33|sT#!5-MsBQ|fpxRI7c%fg$8dcKMWe0Kl% z5&ro-HQiOeU6N*GaPWJz@Xp;^$)vl2N`-Y+6Y>aJpuz5qRzjJ6dWpvbc+4+Vzlz!+ zMa$YdGf{^1e)cq$COm-0*!-aHVF}nYbz{GW)v>Gr)~Kp70Mb8(Y(ZihSi|qF5 z089q9BJI!Buu9C!yR2*Y2q4kcM{t?tq@|G|_%<@ea>STGXz2%?AASW~uXEq{Br=wk z;iYtbm+uz4>eazwD!eYWHz5TL$FioIQmm#<0q=S&yGv%>(jRr+j0xVP4fwW~TW!&C zW;FK}vhuHx>NIf;<_bI%=cHBC$gQaA$55KdxcRQYC}{A?n*LFZVSxOh>9RMUq!p+1 z3b+o2kA(^lme;OnzCpiD>d8gsM4FWk<_TASAE>{y?UnzI-kfutXG!&%xG*OQYE5*F zKRZ&$x^-pS>w0-i6XiYyMz`?ph1BT6l;^LoTMlfY1M1dsU~3NdWv|JT*W!B*rE?zN zL$=&u)^hz_W=Q*Hu=D)oB7Utxr|bE&BI={s8ij4!u?rlcer>!d<3W$RcL9~X;OWqh zSOiRkO`m12Srj~HGB&B)ExJ7|u50z<(mvj`L@%c-=D=^^l(TR?pzXQK52^Y;==qY< zbRwd8@ak?QQX2^_l?sygrJC<#-Opg|dNb$inQC298xt1{gp4!Wo&@1F_^@xEwSV(I0PKsI}kIF$b$=b-aygh z_b$B~T;22GMW4NvE`H-P(UguY{5O4^L-@Y)A^35c5x&<@_XlVuj^_#=jcOblZG9 zdFXYD{dweuA(en;gvv?Zj!k?tAC0ob&U7=9LnCI(7O$!wjHZbdX?2R^6+HWEZ%V9% zo*v1!(M=0%3%Va$Tnb&|yXAO!r=M81O3%#UKV2`L?dh#%H&0!C9C)}_jHl$DG`ufC zGqzclc(&4Bj`#B)7r?LJDesZEAF2vUhtdD~;y3HR z2K}eo-2b>8-t@0;kN*oyG18C App); +// It also ensures that whether you load the app in Expo Go or in a native build, +// the environment is set up appropriately +registerRootComponent(App); diff --git a/mobile-app_legacy_rn_expo/package-lock.json b/mobile-app_legacy_rn_expo/package-lock.json new file mode 100644 index 00000000..974f1522 --- /dev/null +++ b/mobile-app_legacy_rn_expo/package-lock.json @@ -0,0 +1,9524 @@ +{ + "name": "mobile-app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mobile-app", + "version": "1.0.0", + "dependencies": { + "@react-navigation/bottom-tabs": "^7.4.7", + "@react-navigation/native": "^7.1.18", + "@react-navigation/native-stack": "^7.3.25", + "axios": "^1.12.2", + "expo": "~54.0.33", + "expo-constants": "~18.0.13", + "expo-localization": "~17.0.8", + "expo-secure-store": "~15.0.8", + "expo-status-bar": "~3.0.9", + "i18next": "^25.5.2", + "react": "19.1.0", + "react-i18next": "^16.0.0", + "react-native": "0.81.5", + "react-native-gesture-handler": "~2.28.0", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0" + }, + "devDependencies": { + "@types/react": "~19.1.0", + "typescript": "~5.9.2" + } + }, + "node_modules/@0no-co/graphql.web": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", + "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", + "license": "MIT", + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.29.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz", + "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-decorators": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-default-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.27.1.tgz", + "integrity": "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", + "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-default-from": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", + "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", + "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", + "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-flow": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", + "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", + "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", + "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", + "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", + "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.28.0", + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/plugin-transform-react-jsx-development": "^7.27.1", + "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse--for-generate-function-map": { + "name": "@babel/traverse", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse--for-generate-function-map/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "license": "MIT", + "dependencies": { + "@types/hammerjs": "^2.0.36" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@expo/code-signing-certificates": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz", + "integrity": "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==", + "license": "MIT", + "dependencies": { + "node-forge": "^1.3.3" + } + }, + "node_modules/@expo/config": { + "version": "12.0.13", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.13.tgz", + "integrity": "sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "@expo/config-plugins": "~54.0.4", + "@expo/config-types": "^54.0.10", + "@expo/json-file": "^10.0.8", + "deepmerge": "^4.3.1", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4", + "sucrase": "~3.35.1" + } + }, + "node_modules/@expo/config-plugins": { + "version": "54.0.4", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.4.tgz", + "integrity": "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==", + "license": "MIT", + "dependencies": { + "@expo/config-types": "^54.0.10", + "@expo/json-file": "~10.0.8", + "@expo/plist": "^0.4.8", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/@expo/config-plugins/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/config-plugins/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/config-plugins/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/config-plugins/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/config-plugins/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/config-plugins/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/config-types": { + "version": "54.0.10", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.10.tgz", + "integrity": "sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==", + "license": "MIT" + }, + "node_modules/@expo/devcert": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@expo/devcert/-/devcert-1.2.1.tgz", + "integrity": "sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==", + "license": "MIT", + "dependencies": { + "@expo/sudo-prompt": "^9.3.1", + "debug": "^3.1.0" + } + }, + "node_modules/@expo/devcert/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@expo/devtools": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@expo/devtools/-/devtools-0.1.8.tgz", + "integrity": "sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@expo/devtools/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/devtools/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/devtools/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/devtools/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/devtools/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/devtools/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/env": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.11.tgz", + "integrity": "sha512-xV+ps6YCW7XIPVUwFVCRN2nox09dnRwy8uIjwHWTODu0zFw4kp4omnVkl0OOjuu2XOe7tdgAHxikrkJt9xB/7Q==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "debug": "^4.3.4", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0" + } + }, + "node_modules/@expo/env/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/env/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/env/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/env/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/env/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/env/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/fingerprint": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.15.5.tgz", + "integrity": "sha512-mdVoAMcux1WlM6kd1RoWiHRNqKqS+J6mKmWQ/BKgeh937S/fcW58EE68O6nc4KDXtWi3PBeNHskOFcgyIuD4hw==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "arg": "^5.0.2", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "ignore": "^5.3.1", + "minimatch": "^10.2.2", + "p-limit": "^3.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0" + }, + "bin": { + "fingerprint": "bin/cli.js" + } + }, + "node_modules/@expo/fingerprint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/fingerprint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/fingerprint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/fingerprint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/fingerprint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/fingerprint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/image-utils": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.8.14.tgz", + "integrity": "sha512-5Sn+jG4Cw+shC2wDMXoqSAJnvERbiwzHn05FpWtD5IBflfTIs5gUmjzwiGVyjOdlMSQhgRrw/AymPbmO9h9mpQ==", + "license": "MIT", + "dependencies": { + "@expo/require-utils": "^55.0.5", + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "getenv": "^2.0.0", + "jimp-compact": "0.16.1", + "parse-png": "^2.1.0", + "semver": "^7.6.0" + } + }, + "node_modules/@expo/image-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/image-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/image-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/image-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/image-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/image-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/json-file": { + "version": "10.0.14", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.14.tgz", + "integrity": "sha512-yWwBFywFv+SxkJp/pIzzA416JVYflNUh7pqQzgaA6nXDqRyK7KfrqVzk8PdUfDnqbBcaZZxpzNssfQZzp5KHrA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "json5": "^2.2.3" + } + }, + "node_modules/@expo/json-file/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@expo/metro": { + "version": "54.2.0", + "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.2.0.tgz", + "integrity": "sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w==", + "license": "MIT", + "dependencies": { + "metro": "0.83.3", + "metro-babel-transformer": "0.83.3", + "metro-cache": "0.83.3", + "metro-cache-key": "0.83.3", + "metro-config": "0.83.3", + "metro-core": "0.83.3", + "metro-file-map": "0.83.3", + "metro-minify-terser": "0.83.3", + "metro-resolver": "0.83.3", + "metro-runtime": "0.83.3", + "metro-source-map": "0.83.3", + "metro-symbolicate": "0.83.3", + "metro-transform-plugins": "0.83.3", + "metro-transform-worker": "0.83.3" + } + }, + "node_modules/@expo/osascript": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.4.3.tgz", + "integrity": "sha512-wbuj3EebM7W9hN/Wp4xTzKd6rQ2zKJzAxkFxkOOwyysLp0HOAgQ4/5RINyoS241pZUX2rUHq7mAJ7pcCQ8U0Ow==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/package-manager": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.10.5.tgz", + "integrity": "sha512-nCP9Mebfl3jvOr0/P6VAuyah6PAtun+aihIL2zAtuE8uSe94JWkVZ7051i0MUVO+y3gFpBqnr8IIH5ch+VJjHA==", + "license": "MIT", + "dependencies": { + "@expo/json-file": "^10.0.14", + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.0.0", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "resolve-workspace-root": "^2.0.0" + } + }, + "node_modules/@expo/package-manager/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/package-manager/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/package-manager/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/package-manager/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/package-manager/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/package-manager/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/plist": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.8.tgz", + "integrity": "sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.2.3", + "xmlbuilder": "^15.1.1" + } + }, + "node_modules/@expo/require-utils": { + "version": "55.0.5", + "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-55.0.5.tgz", + "integrity": "sha512-U4K/CQ2VpXuwfNGsN+daKmYOt15hCP8v/pXaYH6eut7kdYZo6SfJ1yr67BIcJ+1Gzzs+QzTxswAZChKpXmceyw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "@babel/core": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@expo/require-utils/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@expo/schema-utils": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@expo/schema-utils/-/schema-utils-0.1.8.tgz", + "integrity": "sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A==", + "license": "MIT" + }, + "node_modules/@expo/sdk-runtime-versions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz", + "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", + "license": "MIT" + }, + "node_modules/@expo/spawn-async": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", + "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@expo/sudo-prompt": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz", + "integrity": "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==", + "license": "MIT" + }, + "node_modules/@expo/ws-tunnel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.6.tgz", + "integrity": "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==", + "license": "MIT" + }, + "node_modules/@expo/xcpretty": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.4.4.tgz", + "integrity": "sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "chalk": "^4.1.0", + "js-yaml": "^4.1.0" + }, + "bin": { + "excpretty": "build/cli.js" + } + }, + "node_modules/@expo/xcpretty/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@expo/xcpretty/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@expo/xcpretty/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/@expo/xcpretty/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@expo/xcpretty/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@expo/xcpretty/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@expo/xcpretty/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@expo/xcpretty/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@expo/xcpretty/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@react-native/assets-registry": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz", + "integrity": "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/babel-plugin-codegen": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.5.tgz", + "integrity": "sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.3", + "@react-native/codegen": "0.81.5" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/babel-preset": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.81.5.tgz", + "integrity": "sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-classes": "^7.25.4", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/template": "^7.25.0", + "@react-native/babel-plugin-codegen": "0.81.5", + "babel-plugin-syntax-hermes-parser": "0.29.1", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/codegen": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.5.tgz", + "integrity": "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.25.3", + "glob": "^7.1.1", + "hermes-parser": "0.29.1", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/codegen/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/@react-native/codegen/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@react-native/codegen/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@react-native/codegen/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT" + }, + "node_modules/@react-native/codegen/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "node_modules/@react-native/codegen/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz", + "integrity": "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==", + "license": "MIT", + "dependencies": { + "@react-native/dev-middleware": "0.81.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.83.1", + "metro-config": "^0.83.1", + "metro-core": "^0.83.1", + "semver": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "node_modules/@react-native/debugger-frontend": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz", + "integrity": "sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.5.tgz", + "integrity": "sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA==", + "license": "MIT", + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.81.5", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.2.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^6.2.3" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz", + "integrity": "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz", + "integrity": "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz", + "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", + "license": "MIT" + }, + "node_modules/@react-navigation/bottom-tabs": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.16.0.tgz", + "integrity": "sha512-ShHaAR2mQxsSzL2kDRWfquomRsFfG+bm+UBRgu8D9ScbNBnnetzwOBestz/1DugVj89WtNapWJuHbMdgyCZEyQ==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.9.17", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^7.2.4", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/core": { + "version": "7.17.4", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.17.4.tgz", + "integrity": "sha512-Rv9E2oNNQEkPGpmu9q+vJwGJRSQR6LBg5L+Yo1QHjtwGbHUbjkIKOdYymDZoZYgNzX2OD4rAIlfuzbDKa3cCeA==", + "license": "MIT", + "dependencies": { + "@react-navigation/routers": "^7.5.5", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "query-string": "^7.1.3", + "react-is": "^19.1.0", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "react": ">= 18.2.0" + } + }, + "node_modules/@react-navigation/core/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-navigation/core/node_modules/react-is": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", + "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", + "license": "MIT" + }, + "node_modules/@react-navigation/elements": { + "version": "2.9.17", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.17.tgz", + "integrity": "sha512-Prax9RDS6l32npcl4PzvL88VoXe9HdtcIUP2+rim3DLVSZceD6oreA+cmPBUjeLFjsnxKlU3pTRby3RpYJ5/xw==", + "license": "MIT", + "dependencies": { + "color": "^4.2.3", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@react-native-masked-view/masked-view": ">= 0.2.0", + "@react-navigation/native": "^7.2.4", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0" + }, + "peerDependenciesMeta": { + "@react-native-masked-view/masked-view": { + "optional": true + } + } + }, + "node_modules/@react-navigation/native": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.4.tgz", + "integrity": "sha512-eWC2D3JjhYLId2fVTZhhCiUpWIaPhO9XyEb7Wq8ElmOHyIODlbOzgZ0rKia02OIsDKr9BzZl2sK1dL70yMxDaw==", + "license": "MIT", + "dependencies": { + "@react-navigation/core": "^7.17.4", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "use-latest-callback": "^0.2.4" + }, + "peerDependencies": { + "react": ">= 18.2.0", + "react-native": "*" + } + }, + "node_modules/@react-navigation/native-stack": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.15.0.tgz", + "integrity": "sha512-2DStedhazfj+IlUrCabMib0PjroyxYDXLWUyxM7e0+vTafhCA2ID9kr4smn/GbXXbjZo0u4OfwJPRujOeaj8rg==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.9.17", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0", + "warn-once": "^0.1.1" + }, + "peerDependencies": { + "@react-navigation/native": "^7.2.4", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/native/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-navigation/routers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.5.tgz", + "integrity": "sha512-9/hhMte12Kgu+pMnLfA4EWJ0OQmIEAMVMX06FPH2yGkEQSQ3JhhCN/GkcRikzQhtEi97VYYQA15umptBUShcOQ==", + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hammerjs": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", + "integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", + "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "license": "ISC" + }, + "node_modules/@urql/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", + "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.13", + "wonka": "^6.3.2" + } + }, + "node_modules/@urql/exchange-retry": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.2.tgz", + "integrity": "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==", + "license": "MIT", + "dependencies": { + "@urql/core": "^5.1.2", + "wonka": "^6.3.2" + }, + "peerDependencies": { + "@urql/core": "^5.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-react-compiler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", + "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + } + }, + "node_modules/babel-plugin-react-native-web": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz", + "integrity": "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==", + "license": "MIT" + }, + "node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", + "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", + "license": "MIT", + "dependencies": { + "hermes-parser": "0.29.1" + } + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT" + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "node_modules/babel-plugin-transform-flow-enums": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", + "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-flow": "^7.12.1" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/better-opn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "license": "MIT", + "dependencies": { + "open": "^8.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/better-opn/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-creator": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", + "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", + "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-edge-launcher": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", + "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "node_modules/chromium-edge-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/env-editor": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", + "integrity": "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/expo": { + "version": "54.0.34", + "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.34.tgz", + "integrity": "sha512-XkVHguZZDC8BcTQxHAd14/TQFbDp1Wt0Z/KApO9t68Ll5A127hLCPzU+a9gytfCIiyL/V1IpF1vIcOLKEVAoNQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "@expo/cli": "54.0.24", + "@expo/config": "~12.0.13", + "@expo/config-plugins": "~54.0.4", + "@expo/devtools": "0.1.8", + "@expo/fingerprint": "0.15.5", + "@expo/metro": "~54.2.0", + "@expo/metro-config": "54.0.15", + "@expo/vector-icons": "^15.0.3", + "@ungap/structured-clone": "^1.3.0", + "babel-preset-expo": "~54.0.10", + "expo-asset": "~12.0.13", + "expo-constants": "~18.0.13", + "expo-file-system": "~19.0.22", + "expo-font": "~14.0.11", + "expo-keep-awake": "~15.0.8", + "expo-modules-autolinking": "3.0.25", + "expo-modules-core": "3.0.30", + "pretty-format": "^29.7.0", + "react-refresh": "^0.14.2", + "whatwg-url-without-unicode": "8.0.0-3" + }, + "bin": { + "expo": "bin/cli", + "expo-modules-autolinking": "bin/autolinking", + "fingerprint": "bin/fingerprint" + }, + "peerDependencies": { + "@expo/dom-webview": "*", + "@expo/metro-runtime": "*", + "react": "*", + "react-native": "*", + "react-native-webview": "*" + }, + "peerDependenciesMeta": { + "@expo/dom-webview": { + "optional": true + }, + "@expo/metro-runtime": { + "optional": true + }, + "react-native-webview": { + "optional": true + } + } + }, + "node_modules/expo-constants": { + "version": "18.0.13", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.13.tgz", + "integrity": "sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.13", + "@expo/env": "~2.0.8" + }, + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, + "node_modules/expo-localization": { + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-17.0.8.tgz", + "integrity": "sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==", + "license": "MIT", + "dependencies": { + "rtl-detect": "^1.0.2" + }, + "peerDependencies": { + "expo": "*", + "react": "*" + } + }, + "node_modules/expo-modules-autolinking": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.25.tgz", + "integrity": "sha512-YmHWctJlwvOuLZccg3cOXvSiXVJrPMKl7g2YR0YHWoGL9v2RvcmgaPJWPSLVW+voNEgEPsbo5UmUrAqbnYcBeg==", + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-modules-autolinking": "bin/expo-modules-autolinking.js" + } + }, + "node_modules/expo-modules-autolinking/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expo-modules-autolinking/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expo-modules-autolinking/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expo-modules-autolinking/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/expo-modules-autolinking/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo-modules-autolinking/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expo-modules-core": { + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.30.tgz", + "integrity": "sha512-a6IrpAn/Jbmwxi9L+hMmXKpNqnkUpoF7WHOpn02rVLyax2J0gB1vvCVE5rNydplEnt41Q6WxQwvcOjZaIkcSUg==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-secure-store": { + "version": "15.0.8", + "resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-15.0.8.tgz", + "integrity": "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-server": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.6.tgz", + "integrity": "sha512-vb5TBtskvEdzYuW79lATXutOEBfW5m6U4EFpNjCVZTnI7S//SAsLQkYEpn+EDfn84m6VQfzSGkIVR6YPaScKFA==", + "license": "MIT", + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/expo-status-bar": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-3.0.9.tgz", + "integrity": "sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==", + "license": "MIT", + "dependencies": { + "react-native-is-edge-to-edge": "^1.2.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/expo/node_modules/@expo/cli": { + "version": "54.0.24", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.24.tgz", + "integrity": "sha512-5xse1bEgnVUBhOrtttc6xTNJVvjyTRavpzuF0/0nuj+312vfSbk7EiRbG+xJ2pW/iZxnhLPJkFCrPYG0nmheAQ==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.8", + "@expo/code-signing-certificates": "^0.0.6", + "@expo/config": "~12.0.13", + "@expo/config-plugins": "~54.0.4", + "@expo/devcert": "^1.2.1", + "@expo/env": "~2.0.8", + "@expo/image-utils": "^0.8.8", + "@expo/json-file": "^10.0.8", + "@expo/metro": "~54.2.0", + "@expo/metro-config": "~54.0.15", + "@expo/osascript": "^2.3.8", + "@expo/package-manager": "^1.9.10", + "@expo/plist": "^0.4.8", + "@expo/prebuild-config": "^54.0.8", + "@expo/schema-utils": "^0.1.8", + "@expo/spawn-async": "^1.7.2", + "@expo/ws-tunnel": "^1.0.1", + "@expo/xcpretty": "^4.3.0", + "@react-native/dev-middleware": "0.81.5", + "@urql/core": "^5.0.6", + "@urql/exchange-retry": "^1.3.0", + "accepts": "^1.3.8", + "arg": "^5.0.2", + "better-opn": "~3.0.2", + "bplist-creator": "0.1.0", + "bplist-parser": "^0.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.3.0", + "compression": "^1.7.4", + "connect": "^3.7.0", + "debug": "^4.3.4", + "env-editor": "^0.4.1", + "expo-server": "^1.0.6", + "freeport-async": "^2.0.0", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "lan-network": "^0.2.1", + "minimatch": "^9.0.0", + "node-forge": "^1.3.3", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "picomatch": "^4.0.3", + "pretty-bytes": "^5.6.0", + "pretty-format": "^29.7.0", + "progress": "^2.0.3", + "prompts": "^2.3.2", + "qrcode-terminal": "0.11.0", + "require-from-string": "^2.0.2", + "requireg": "^0.2.2", + "resolve": "^1.22.2", + "resolve-from": "^5.0.0", + "resolve.exports": "^2.0.3", + "semver": "^7.6.0", + "send": "^0.19.0", + "slugify": "^1.3.4", + "source-map-support": "~0.5.21", + "stacktrace-parser": "^0.1.10", + "structured-headers": "^0.4.1", + "tar": "^7.5.2", + "terminal-link": "^2.1.1", + "undici": "^6.18.2", + "wrap-ansi": "^7.0.0", + "ws": "^8.12.1" + }, + "bin": { + "expo-internal": "build/bin/cli" + }, + "peerDependencies": { + "expo": "*", + "expo-router": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo-router": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/prebuild-config": { + "version": "54.0.8", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-54.0.8.tgz", + "integrity": "sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.13", + "@expo/config-plugins": "~54.0.4", + "@expo/config-types": "^54.0.10", + "@expo/image-utils": "^0.8.8", + "@expo/json-file": "^10.0.8", + "@react-native/normalize-colors": "0.81.5", + "debug": "^4.3.1", + "resolve-from": "^5.0.0", + "semver": "^7.6.0", + "xml2js": "0.6.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo/node_modules/@expo/metro-config": { + "version": "54.0.15", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.15.tgz", + "integrity": "sha512-SqIya4VZ9KHM1S9g+xR0A+QKw1Tfs7Gacx6bQNJ98vs4+O7I5+QP5mHZIB0QSZLUV8opiXebHYTiTu+0OAsIUw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.20.0", + "@babel/core": "^7.20.0", + "@babel/generator": "^7.20.5", + "@expo/config": "~12.0.13", + "@expo/env": "~2.0.8", + "@expo/json-file": "~10.0.8", + "@expo/metro": "~54.2.0", + "@expo/spawn-async": "^1.7.2", + "browserslist": "^4.25.0", + "chalk": "^4.1.0", + "debug": "^4.3.2", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "hermes-parser": "^0.29.1", + "jsc-safe-url": "^0.2.4", + "lightningcss": "^1.30.1", + "picomatch": "^4.0.3", + "postcss": "~8.4.32", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "expo": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, + "node_modules/expo/node_modules/@expo/vector-icons": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.1.1.tgz", + "integrity": "sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==", + "license": "MIT", + "peerDependencies": { + "expo-font": ">=14.0.4", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expo/node_modules/babel-preset-expo": { + "version": "54.0.10", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz", + "integrity": "sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/plugin-proposal-decorators": "^7.12.9", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@react-native/babel-preset": "0.81.5", + "babel-plugin-react-compiler": "^1.0.0", + "babel-plugin-react-native-web": "~0.21.0", + "babel-plugin-syntax-hermes-parser": "^0.29.1", + "babel-plugin-transform-flow-enums": "^0.0.2", + "debug": "^4.3.4", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "@babel/runtime": "^7.20.0", + "expo": "*", + "react-refresh": ">=0.14.0 <1.0.0" + }, + "peerDependenciesMeta": { + "@babel/runtime": { + "optional": true + }, + "expo": { + "optional": true + } + } + }, + "node_modules/expo/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/expo/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/expo/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expo/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expo/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/expo/node_modules/expo-asset": { + "version": "12.0.13", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.13.tgz", + "integrity": "sha512-x/p7WvQUnkn6K43b9eL6SPeq5Vnf1E8BDe9bDrWrvMqzyUvJnUFvl+ctg3034s/+UHe7Ne2pAmc0+yzbl8CrDQ==", + "license": "MIT", + "dependencies": { + "@expo/image-utils": "^0.8.8", + "expo-constants": "~18.0.13" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/expo-file-system": { + "version": "19.0.22", + "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.22.tgz", + "integrity": "sha512-l9pgahSc7sJD0bP9vBNeXvZjy8QKDpVHVxWmei/ESQOrzmoj5BidziqLVsyZdxsi+PfdbTtttLTAmddH/JafYA==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/expo-font": { + "version": "14.0.11", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", + "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", + "license": "MIT", + "dependencies": { + "fontfaceobserver": "^2.1.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo/node_modules/expo-keep-awake": { + "version": "15.0.8", + "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz", + "integrity": "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*" + } + }, + "node_modules/expo/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT" + }, + "node_modules/expo/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "node_modules/expo/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/expo/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/expo/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expo/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontfaceobserver": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", + "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", + "license": "BSD-2-Clause" + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/freeport-async": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", + "integrity": "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.32.0" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/i18next": { + "version": "25.10.10", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.10.10.tgz", + "integrity": "sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jimp-compact": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", + "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsc-safe-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lan-network": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.2.1.tgz", + "integrity": "sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==", + "license": "MIT", + "bin": { + "lan-network": "dist/lan-network-cli.js" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/metro": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", + "integrity": "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.3", + "@babel/types": "^7.25.2", + "accepts": "^1.3.7", + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.32.0", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.83.3", + "metro-cache": "0.83.3", + "metro-cache-key": "0.83.3", + "metro-config": "0.83.3", + "metro-core": "0.83.3", + "metro-file-map": "0.83.3", + "metro-resolver": "0.83.3", + "metro-runtime": "0.83.3", + "metro-source-map": "0.83.3", + "metro-symbolicate": "0.83.3", + "metro-transform-plugins": "0.83.3", + "metro-transform-worker": "0.83.3", + "mime-types": "^2.1.27", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz", + "integrity": "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.32.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.3.tgz", + "integrity": "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==", + "license": "MIT", + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.83.3" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache-key": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.3.tgz", + "integrity": "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-config": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.3.tgz", + "integrity": "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==", + "license": "MIT", + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.83.3", + "metro-cache": "0.83.3", + "metro-core": "0.83.3", + "metro-runtime": "0.83.3", + "yaml": "^2.6.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-core": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.3.tgz", + "integrity": "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.83.3" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-file-map": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.3.tgz", + "integrity": "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz", + "integrity": "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-resolver": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.3.tgz", + "integrity": "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-runtime": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.3.tgz", + "integrity": "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-source-map": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.3.tgz", + "integrity": "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.3", + "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", + "@babel/types": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.83.3", + "nullthrows": "^1.1.1", + "ob1": "0.83.3", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz", + "integrity": "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.83.3", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz", + "integrity": "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.3", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz", + "integrity": "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/types": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "metro": "0.83.3", + "metro-babel-transformer": "0.83.3", + "metro-cache": "0.83.3", + "metro-cache-key": "0.83.3", + "metro-minify-terser": "0.83.3", + "metro-source-map": "0.83.3", + "metro-transform-plugins": "0.83.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro/node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/metro/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/metro/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nested-error-stacks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", + "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", + "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT" + }, + "node_modules/ob1": { + "version": "0.83.3", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.3.tgz", + "integrity": "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==", + "license": "MIT", + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-png": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", + "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", + "license": "MIT", + "dependencies": { + "pngjs": "^3.3.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/plist/node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode-terminal": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", + "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-freeze": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", + "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=17.0.0" + } + }, + "node_modules/react-i18next": { + "version": "16.6.6", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.6.6.tgz", + "integrity": "sha512-ZgL2HUoW34UKUkOV7uSQFE1CDnRPD+tCR3ywSuWH7u2iapnz86U8Bi3Vrs620qNDzCf1F47NxglCEkchCTDOHw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 25.10.9", + "react": ">= 16.8.0", + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-native": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", + "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", + "license": "MIT", + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@react-native/assets-registry": "0.81.5", + "@react-native/codegen": "0.81.5", + "@react-native/community-cli-plugin": "0.81.5", + "@react-native/gradle-plugin": "0.81.5", + "@react-native/js-polyfills": "0.81.5", + "@react-native/normalize-colors": "0.81.5", + "@react-native/virtualized-lists": "0.81.5", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "0.29.1", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "glob": "^7.1.1", + "invariant": "^2.2.4", + "jest-environment-node": "^29.7.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.83.1", + "metro-source-map": "^0.83.1", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.26.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0", + "ws": "^6.2.3", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.0", + "react": "^19.1.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native-gesture-handler": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz", + "integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==", + "license": "MIT", + "dependencies": { + "@egjs/hammerjs": "^2.0.17", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-is-edge-to-edge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz", + "integrity": "sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-safe-area-context": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", + "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-screens": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", + "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", + "license": "MIT", + "dependencies": { + "react-freeze": "^1.0.0", + "react-native-is-edge-to-edge": "^1.2.1", + "warn-once": "^0.1.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.81.5", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", + "integrity": "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/react-native/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/react-native/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/react-native/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-native/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/react-native/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requireg": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", + "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", + "dependencies": { + "nested-error-stacks": "~2.0.1", + "rc": "~1.2.7", + "resolve": "~1.7.1" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/requireg/node_modules/resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.5" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-workspace-root": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.1.tgz", + "integrity": "sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==", + "license": "MIT" + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rtl-detect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", + "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sf-symbols-typescript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz", + "integrity": "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-plist": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", + "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "license": "MIT", + "dependencies": { + "bplist-creator": "0.1.0", + "bplist-parser": "0.3.1", + "plist": "^3.0.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slugify": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz", + "integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/structured-headers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", + "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "7.5.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", + "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", + "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz", + "integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-latest-callback": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", + "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT" + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warn-once": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", + "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/whatwg-url-without-unicode": { + "version": "8.0.0-3", + "resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz", + "integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==", + "license": "MIT", + "dependencies": { + "buffer": "^5.4.3", + "punycode": "^2.1.1", + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wonka": { + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.6.tgz", + "integrity": "sha512-MXH+6mDHAZ2GuMpgKS055FR6v0xVP3XwquxIMYXgiW+FejHQlMGlvVRZT4qMCxR+bEo/FCtIdKxwej9WV3YQag==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xcode": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", + "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "license": "Apache-2.0", + "dependencies": { + "simple-plist": "^1.1.0", + "uuid": "^7.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", + "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/mobile-app_legacy_rn_expo/package.json b/mobile-app_legacy_rn_expo/package.json new file mode 100644 index 00000000..f0acf6ca --- /dev/null +++ b/mobile-app_legacy_rn_expo/package.json @@ -0,0 +1,34 @@ +{ + "name": "mobile-app", + "version": "1.0.0", + "main": "index.ts", + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web" + }, + "dependencies": { + "@react-navigation/bottom-tabs": "^7.4.7", + "@react-navigation/native": "^7.1.18", + "@react-navigation/native-stack": "^7.3.25", + "axios": "^1.12.2", + "expo": "~54.0.33", + "expo-constants": "~18.0.13", + "expo-localization": "~17.0.8", + "expo-secure-store": "~15.0.8", + "expo-status-bar": "~3.0.9", + "i18next": "^25.5.2", + "react": "19.1.0", + "react-i18next": "^16.0.0", + "react-native": "0.81.5", + "react-native-gesture-handler": "~2.28.0", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0" + }, + "devDependencies": { + "@types/react": "~19.1.0", + "typescript": "~5.9.2" + }, + "private": true +} diff --git a/mobile-app_legacy_rn_expo/src/api/apiClient.ts b/mobile-app_legacy_rn_expo/src/api/apiClient.ts new file mode 100644 index 00000000..54e71aa5 --- /dev/null +++ b/mobile-app_legacy_rn_expo/src/api/apiClient.ts @@ -0,0 +1,28 @@ +import axios from 'axios'; + +import { backendBaseUrl } from '../config/env'; +import { authStore } from '../state/auth/authStore'; + +export const apiClient = axios.create({ + baseURL: `${backendBaseUrl}/api`, + timeout: 60000, + maxRedirects: 5, + validateStatus: status => status >= 200 && status < 600, +}); + +apiClient.interceptors.request.use(config => { + const token = authStore.getToken(); + const username = authStore.getUsername(); + + if (token) { + config.headers = config.headers || {}; + config.headers['authcode'] = token; + } + if (username) { + config.headers = config.headers || {}; + config.headers['userid'] = username; + } + + return config; +}); + diff --git a/mobile-app_legacy_rn_expo/src/config/env.ts b/mobile-app_legacy_rn_expo/src/config/env.ts new file mode 100644 index 00000000..cf543bb9 --- /dev/null +++ b/mobile-app_legacy_rn_expo/src/config/env.ts @@ -0,0 +1,17 @@ +import Constants from 'expo-constants'; + +export type AppEnv = 'development' | 'staging' | 'production'; + +function normalizeEnv(value: unknown): AppEnv { + const env = String(value || '').toLowerCase(); + if (env === 'staging') return 'staging'; + if (env === 'production') return 'production'; + return 'development'; +} + +export const appEnv: AppEnv = normalizeEnv(Constants.expoConfig?.extra?.APP_ENV); + +export const backendBaseUrl: string = + (Constants.expoConfig?.extra?.BACKEND_BASE_URL as string | undefined) || + 'http://localhost:3005'; + diff --git a/mobile-app_legacy_rn_expo/src/i18n/index.ts b/mobile-app_legacy_rn_expo/src/i18n/index.ts new file mode 100644 index 00000000..9786bd32 --- /dev/null +++ b/mobile-app_legacy_rn_expo/src/i18n/index.ts @@ -0,0 +1,30 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import * as Localization from 'expo-localization'; + +import de from '../../../frontend/src/i18n/locales/de.json'; +import enUS from '../../../frontend/src/i18n/locales/en-US.json'; + +const resources = { + de: { translation: de }, + 'en-US': { translation: enUS }, +} as const; + +function pickLanguageTag() { + const tag = Localization.getLocales()?.[0]?.languageTag || 'de'; + if (tag in resources) return tag as keyof typeof resources; + if (tag.startsWith('en')) return 'en-US'; + return 'de'; +} + +void i18n + .use(initReactI18next) + .init({ + resources, + lng: pickLanguageTag(), + fallbackLng: 'de', + interpolation: { escapeValue: false }, + }); + +export default i18n; + diff --git a/mobile-app_legacy_rn_expo/src/navigation/AppNavigation.tsx b/mobile-app_legacy_rn_expo/src/navigation/AppNavigation.tsx new file mode 100644 index 00000000..31ed5460 --- /dev/null +++ b/mobile-app_legacy_rn_expo/src/navigation/AppNavigation.tsx @@ -0,0 +1,43 @@ +import { NavigationContainer } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; + +import { useAuth } from '../state/auth/AuthContext'; +import { HomeScreen } from '../screens/HomeScreen'; +import { LoginScreen } from '../screens/LoginScreen'; + +type AuthStackParamList = { + Login: undefined; +}; + +type AppTabsParamList = { + Home: undefined; +}; + +const AuthStack = createNativeStackNavigator(); +const Tabs = createBottomTabNavigator(); + +function AppTabs() { + return ( + + + + ); +} + +export function AppNavigation() { + const { token } = useAuth(); + + return ( + + {token ? ( + + ) : ( + + + + )} + + ); +} + diff --git a/mobile-app_legacy_rn_expo/src/screens/HomeScreen.tsx b/mobile-app_legacy_rn_expo/src/screens/HomeScreen.tsx new file mode 100644 index 00000000..4e3b982f --- /dev/null +++ b/mobile-app_legacy_rn_expo/src/screens/HomeScreen.tsx @@ -0,0 +1,29 @@ +import { Button, StyleSheet, Text, View } from 'react-native'; +import { useTranslation } from 'react-i18next'; + +import { useAuth } from '../state/auth/AuthContext'; +import { backendBaseUrl } from '../config/env'; + +export function HomeScreen() { + const { t } = useTranslation(); + const { username, logout } = useAuth(); + + return ( + + {t('app.title')} + + {t('common.loading')} {username ? `(${username})` : ''} + + API: {backendBaseUrl} +