const I18N_AVAILABLE = ["en", "de", "pl", "ru", "fr", "es", "pt"]; const I18N_NAMES = { en: "English", de: "Deutsch", pl: "Polski", ru: "Русский", fr: "Français", es: "Español", pt: "Português" }; const I18N_DATA = { en: { login_title: "Sign in", username: "Username", password: "Password", sign_in: "Sign in", invalid_credentials: "Invalid username or password", logout: "Logout", settings: "Settings", language: "Language", competitions: "Competitions", new_competition: "New competition", competition_name: "Competition name", create: "Create", cancel: "Cancel", role: "Role", system_admin: "System Admin", chief_scorer: "Chief Scorer", scorer: "Scorer", pilots: "Pilots", penalties: "Penalties", members: "Members", rules: "Rules", settings_tab: "Settings", number: "Number", last_name: "Last name", first_name: "First name", country: "Country", balloon_id: "Balloon ID", add_pilot: "Add pilot", import_csv: "Import CSV", export_csv: "Export CSV", flight: "Flight", date: "Date", pilot_number: "Pilot No.", pilot_name: "Pilot name", rule: "Rule", task: "Task", penalty_values: "Penalties", description: "Description", created_by: "Created by", transferred: "Transferred", actions: "Actions", add_penalty: "Add penalty", edit: "Edit", delete: "Delete", save: "Save", confirm_delete: "Delete this entry?", search_rule: "Search rule by number or text", suggested_penalty: "Suggested penalty", escalation: "Escalation behavior", escalation_same: "Stays the same", escalation_doubled: "Doubled each time", escalation_escalate: "Escalates: ", add_member: "Add member", remove: "Remove", add_user: "Add user", users: "Users", display_name: "Display name", is_admin: "Admin", allow_any_scorer_edit: "Allow any scorer to edit penalties", open: "Open", back: "Back", change_password: "Change password", new_password: "New password", csv_paste: "Paste CSV (number,last,first,country,balloon)", no_pilots: "No pilots yet", no_penalties: "No penalties yet", no_members: "No members yet", no_competitions: "No competitions", select_pilot: "Select pilot", rule_number_short: "Rule No.", transferred_only: "Only untransferred", showing_n_of_m: "Showing {n} of {m}", online: "Online", offline: "Offline", forbidden: "Not allowed", save_settings: "Save settings", saved: "Saved", yes: "Yes", no: "No", backend_url: "Backend URL", backend_url_hint: "Leave empty to use the same origin (e.g. http://192.168.0.10:8080)", profile: "Profile", current_password: "Current password", leave_blank_keep: "Leave blank to keep current", username_taken: "Username already taken", prior_penalties: "Prior penalties for this pilot and rule", none: "None", applied: "Applied", apply_by_task: "Apply by task", apply_by_task_explain: "Confirm all open penalties for a task at once. Penalties are only marked applied after confirmation.", apply_n_open: "Apply {n} open", confirm_apply_task: "Mark all {n} open penalties for task '{task}' as applied?", summary: "Summary", penalty_summary: "Penalty summary", rule: "Rule", rule_not_found: "rule not found", close: "Close", count: "#", count_hint: "Number of prior penalties for this pilot and rule", prior_count: "Prior count (this pilot & rule)", search_penalties: "Search penalties…", filter_all: "All", filter_open: "Open only", filter_applied: "Applied only", total: "Total", open: "Open", repeat_password: "Repeat password", password_too_short: "Password must be at least 6 characters", passwords_dont_match: "Passwords do not match", too_many_attempts: "Too many login attempts — please wait a few minutes", profile_username_readonly: "Username can only be changed by a system administrator", profile_displayname_readonly: "Display name can only be changed by a system administrator", must_change_password: "Must change password", confirm_force_password: "Force this user to change their password on next request?", force_password_change: "Force password change", force_password_explain: "An administrator has required you to set a new password before you can continue.", user_not_found: "User not found", show_incidents: "Show incidents", hide_incidents: "Hide incidents", apply_select_task: "Pick a task to start applying its open penalties.", no_open_penalties: "No open penalties to apply.", no_task: "(no task)", start_apply: "Start", step_x_of_y: "Pilot {x} of {y}", pilot: "Pilot", next: "Next", to_overview: "To overview", apply_overview: "Overview", apply_overview_explain: "{n} penalty/penalties will be marked applied on save.", nothing_to_apply: "Nothing to apply.", confirm_save_partial: "Save and apply {n} penalty/penalties reviewed so far?", }, de: { login_title: "Anmelden", username: "Benutzername", password: "Passwort", sign_in: "Anmelden", invalid_credentials: "Falscher Benutzername oder Passwort", logout: "Abmelden", settings: "Einstellungen", language: "Sprache", competitions: "Wettbewerbe", new_competition: "Neuer Wettbewerb", competition_name: "Wettbewerbsname", create: "Erstellen", cancel: "Abbrechen", role: "Rolle", system_admin: "Systemadmin", chief_scorer: "Chief-Scorer", scorer: "Scorer", pilots: "Piloten", penalties: "Strafen", members: "Mitglieder", rules: "Regeln", settings_tab: "Einstellungen", number: "Nummer", last_name: "Nachname", first_name: "Vorname", country: "Land", balloon_id: "Ballon-Kennung", add_pilot: "Pilot hinzufügen", import_csv: "CSV importieren", export_csv: "CSV exportieren", flight: "Fahrt", date: "Datum", pilot_number: "Pilot-Nr.", pilot_name: "Pilotenname", rule: "Regel", task: "Aufgabe", penalty_values: "Strafen", description: "Beschreibung", created_by: "Angelegt von", transferred: "Übertragen", actions: "Aktionen", add_penalty: "Strafe hinzufügen", edit: "Bearbeiten", delete: "Löschen", save: "Speichern", confirm_delete: "Eintrag löschen?", search_rule: "Regel nach Nummer oder Text suchen", suggested_penalty: "Vorgeschlagene Strafe", escalation: "Verhalten bei Wiederholung", escalation_same: "Bleibt gleich", escalation_doubled: "Wird jedes Mal verdoppelt", escalation_escalate: "Höherstufung: ", add_member: "Mitglied hinzufügen", remove: "Entfernen", add_user: "Benutzer anlegen", users: "Benutzer", display_name: "Anzeigename", is_admin: "Admin", allow_any_scorer_edit: "Alle Scorer dürfen Strafen bearbeiten", open: "Öffnen", back: "Zurück", change_password: "Passwort ändern", new_password: "Neues Passwort", csv_paste: "CSV einfügen (Nr,Nachname,Vorname,Land,Ballon)", no_pilots: "Noch keine Piloten", no_penalties: "Noch keine Strafen", no_members: "Noch keine Mitglieder", no_competitions: "Keine Wettbewerbe", select_pilot: "Pilot wählen", rule_number_short: "Regel-Nr.", transferred_only: "Nur nicht übertragene", showing_n_of_m: "{n} von {m}", online: "Online", offline: "Offline", forbidden: "Keine Berechtigung", save_settings: "Einstellungen speichern", saved: "Gespeichert", yes: "Ja", no: "Nein", backend_url: "Backend-URL", backend_url_hint: "Leer lassen für gleichen Ursprung (z.B. http://192.168.0.10:8080)", profile: "Profil", current_password: "Aktuelles Passwort", leave_blank_keep: "Leer lassen um beizubehalten", username_taken: "Benutzername bereits vergeben", prior_penalties: "Frühere Strafen für diesen Piloten und diese Regel", none: "Keine", applied: "Angewendet", apply_by_task: "Pro Task anwenden", apply_by_task_explain: "Bestätige alle offenen Strafen einer Aufgabe gemeinsam. Erst nach Bestätigung gelten die Strafen als angewendet.", apply_n_open: "{n} offene anwenden", confirm_apply_task: "Alle {n} offenen Strafen der Aufgabe '{task}' als angewendet markieren?", summary: "Übersicht", penalty_summary: "Strafen-Übersicht", rule: "Regel", rule_not_found: "Regel nicht gefunden", close: "Schließen", count: "#", count_hint: "Anzahl früherer Strafen für diesen Piloten und diese Regel", prior_count: "Frühere Anzahl (dieser Pilot & Regel)", search_penalties: "Strafen durchsuchen…", filter_all: "Alle", filter_open: "Nur offene", filter_applied: "Nur angewendete", total: "Gesamt", open: "Offen", repeat_password: "Passwort wiederholen", password_too_short: "Passwort muss mindestens 6 Zeichen lang sein", passwords_dont_match: "Passwörter stimmen nicht überein", too_many_attempts: "Zu viele Anmeldeversuche — bitte ein paar Minuten warten", profile_username_readonly: "Der Benutzername kann nur vom Systemadministrator geändert werden", profile_displayname_readonly: "Der Anzeigename kann nur vom Systemadministrator geändert werden", must_change_password: "Passwortwechsel erforderlich", confirm_force_password: "Diesen Benutzer beim nächsten Zugriff zum Passwortwechsel zwingen?", force_password_change: "Passwortwechsel erzwingen", force_password_explain: "Ein Administrator hat festgelegt, dass du ein neues Passwort vergeben musst, bevor du fortfahren kannst.", user_not_found: "Benutzer nicht gefunden", show_incidents: "Vorfälle anzeigen", hide_incidents: "Vorfälle ausblenden", apply_select_task: "Aufgabe wählen, deren offene Strafen angewendet werden sollen.", no_open_penalties: "Keine offenen Strafen zum Anwenden.", no_task: "(ohne Aufgabe)", start_apply: "Start", step_x_of_y: "Pilot {x} von {y}", pilot: "Pilot", next: "Weiter", to_overview: "Zur Übersicht", apply_overview: "Übersicht", apply_overview_explain: "Beim Speichern werden {n} Strafe(n) als angewendet markiert.", nothing_to_apply: "Nichts anzuwenden.", confirm_save_partial: "{n} bisher überprüfte Strafe(n) jetzt speichern und anwenden?", }, pl: { login_title: "Zaloguj się", username: "Nazwa użytkownika", password: "Hasło", sign_in: "Zaloguj", invalid_credentials: "Nieprawidłowy login lub hasło", logout: "Wyloguj", settings: "Ustawienia", language: "Język", competitions: "Zawody", new_competition: "Nowe zawody", competition_name: "Nazwa zawodów", create: "Utwórz", cancel: "Anuluj", role: "Rola", system_admin: "Administrator", chief_scorer: "Chief-Scorer", scorer: "Scorer", pilots: "Piloci", penalties: "Kary", members: "Członkowie", rules: "Zasady", settings_tab: "Ustawienia", number: "Numer", last_name: "Nazwisko", first_name: "Imię", country: "Kraj", balloon_id: "Oznaczenie balonu", add_pilot: "Dodaj pilota", import_csv: "Import CSV", export_csv: "Eksport CSV", flight: "Lot", date: "Data", pilot_number: "Nr pilota", pilot_name: "Imię i nazwisko", rule: "Zasada", task: "Zadanie", penalty_values: "Kary", description: "Opis", created_by: "Wprowadził", transferred: "Przesłano", actions: "Akcje", add_penalty: "Dodaj karę", edit: "Edytuj", delete: "Usuń", save: "Zapisz", confirm_delete: "Usunąć ten wpis?", search_rule: "Szukaj zasady po numerze lub tekście", suggested_penalty: "Sugerowana kara", escalation: "Zachowanie przy powtórzeniu", escalation_same: "Bez zmian", escalation_doubled: "Podwajana za każdym razem", escalation_escalate: "Eskalacja: ", add_member: "Dodaj członka", remove: "Usuń", add_user: "Dodaj użytkownika", users: "Użytkownicy", display_name: "Wyświetlana nazwa", is_admin: "Admin", allow_any_scorer_edit: "Pozwól dowolnemu scorerowi edytować kary", open: "Otwórz", back: "Wstecz", change_password: "Zmień hasło", new_password: "Nowe hasło", csv_paste: "Wklej CSV (nr,nazwisko,imię,kraj,balon)", no_pilots: "Brak pilotów", no_penalties: "Brak kar", no_members: "Brak członków", no_competitions: "Brak zawodów", select_pilot: "Wybierz pilota", rule_number_short: "Nr zasady", transferred_only: "Tylko nieprzesłane", showing_n_of_m: "{n} z {m}", online: "Online", offline: "Offline", forbidden: "Brak uprawnień", save_settings: "Zapisz ustawienia", saved: "Zapisano", yes: "Tak", no: "Nie", }, ru: { login_title: "Вход", username: "Имя пользователя", password: "Пароль", sign_in: "Войти", invalid_credentials: "Неверное имя или пароль", logout: "Выйти", settings: "Настройки", language: "Язык", competitions: "Соревнования", new_competition: "Новое соревнование", competition_name: "Название", create: "Создать", cancel: "Отмена", role: "Роль", system_admin: "Администратор", chief_scorer: "Chief-Scorer", scorer: "Scorer", pilots: "Пилоты", penalties: "Штрафы", members: "Участники", rules: "Правила", settings_tab: "Настройки", number: "Номер", last_name: "Фамилия", first_name: "Имя", country: "Страна", balloon_id: "Регистрация шара", add_pilot: "Добавить пилота", import_csv: "Импорт CSV", export_csv: "Экспорт CSV", flight: "Полёт", date: "Дата", pilot_number: "№ пилота", pilot_name: "Имя пилота", rule: "Правило", task: "Задание", penalty_values: "Штрафы", description: "Описание", created_by: "Автор", transferred: "Передано", actions: "Действия", add_penalty: "Добавить штраф", edit: "Редактировать", delete: "Удалить", save: "Сохранить", confirm_delete: "Удалить запись?", search_rule: "Поиск правила по номеру или тексту", suggested_penalty: "Рекомендованный штраф", escalation: "Поведение при повторе", escalation_same: "Без изменений", escalation_doubled: "Удваивается каждый раз", escalation_escalate: "Эскалация: ", add_member: "Добавить участника", remove: "Удалить", add_user: "Создать пользователя", users: "Пользователи", display_name: "Отображаемое имя", is_admin: "Админ", allow_any_scorer_edit: "Любой Scorer может редактировать штрафы", open: "Открыть", back: "Назад", change_password: "Изменить пароль", new_password: "Новый пароль", csv_paste: "Вставьте CSV (№,фамилия,имя,страна,шар)", no_pilots: "Нет пилотов", no_penalties: "Нет штрафов", no_members: "Нет участников", no_competitions: "Нет соревнований", select_pilot: "Выберите пилота", rule_number_short: "№ правила", transferred_only: "Только непереданные", showing_n_of_m: "{n} из {m}", online: "Онлайн", offline: "Оффлайн", forbidden: "Нет доступа", save_settings: "Сохранить настройки", saved: "Сохранено", yes: "Да", no: "Нет", }, fr: { login_title: "Connexion", username: "Nom d'utilisateur", password: "Mot de passe", sign_in: "Connexion", invalid_credentials: "Identifiants invalides", logout: "Déconnexion", settings: "Paramètres", language: "Langue", competitions: "Compétitions", new_competition: "Nouvelle compétition", competition_name: "Nom", create: "Créer", cancel: "Annuler", role: "Rôle", system_admin: "Administrateur", chief_scorer: "Chief-Scorer", scorer: "Scorer", pilots: "Pilotes", penalties: "Pénalités", members: "Membres", rules: "Règles", settings_tab: "Paramètres", number: "Numéro", last_name: "Nom", first_name: "Prénom", country: "Pays", balloon_id: "Immat. ballon", add_pilot: "Ajouter un pilote", import_csv: "Importer CSV", export_csv: "Exporter CSV", flight: "Vol", date: "Date", pilot_number: "N° pilote", pilot_name: "Nom du pilote", rule: "Règle", task: "Épreuve", penalty_values: "Pénalités", description: "Description", created_by: "Créé par", transferred: "Transféré", actions: "Actions", add_penalty: "Ajouter pénalité", edit: "Modifier", delete: "Supprimer", save: "Enregistrer", confirm_delete: "Supprimer cette entrée ?", search_rule: "Rechercher une règle par numéro ou texte", suggested_penalty: "Pénalité suggérée", escalation: "Comportement en cas de répétition", escalation_same: "Reste identique", escalation_doubled: "Doublée à chaque fois", escalation_escalate: "Escalade : ", add_member: "Ajouter membre", remove: "Retirer", add_user: "Créer un utilisateur", users: "Utilisateurs", display_name: "Nom affiché", is_admin: "Admin", allow_any_scorer_edit: "Tous les scorers peuvent modifier les pénalités", open: "Ouvrir", back: "Retour", change_password: "Changer le mot de passe", new_password: "Nouveau mot de passe", csv_paste: "Coller CSV (n°,nom,prénom,pays,ballon)", no_pilots: "Aucun pilote", no_penalties: "Aucune pénalité", no_members: "Aucun membre", no_competitions: "Aucune compétition", select_pilot: "Choisir un pilote", rule_number_short: "N° règle", transferred_only: "Non transférés uniquement", showing_n_of_m: "{n} sur {m}", online: "En ligne", offline: "Hors ligne", forbidden: "Non autorisé", save_settings: "Enregistrer", saved: "Enregistré", yes: "Oui", no: "Non", }, es: { login_title: "Iniciar sesión", username: "Usuario", password: "Contraseña", sign_in: "Entrar", invalid_credentials: "Usuario o contraseña incorrectos", logout: "Salir", settings: "Ajustes", language: "Idioma", competitions: "Competiciones", new_competition: "Nueva competición", competition_name: "Nombre", create: "Crear", cancel: "Cancelar", role: "Rol", system_admin: "Administrador", chief_scorer: "Chief-Scorer", scorer: "Scorer", pilots: "Pilotos", penalties: "Penalizaciones", members: "Miembros", rules: "Reglas", settings_tab: "Ajustes", number: "Número", last_name: "Apellido", first_name: "Nombre", country: "País", balloon_id: "Matrícula globo", add_pilot: "Añadir piloto", import_csv: "Importar CSV", export_csv: "Exportar CSV", flight: "Vuelo", date: "Fecha", pilot_number: "N.º piloto", pilot_name: "Nombre piloto", rule: "Regla", task: "Tarea", penalty_values: "Penalizaciones", description: "Descripción", created_by: "Creado por", transferred: "Transferido", actions: "Acciones", add_penalty: "Añadir penalización", edit: "Editar", delete: "Eliminar", save: "Guardar", confirm_delete: "¿Eliminar este registro?", search_rule: "Buscar regla por número o texto", suggested_penalty: "Penalización sugerida", escalation: "Comportamiento al repetirse", escalation_same: "Sin cambios", escalation_doubled: "Se duplica cada vez", escalation_escalate: "Escala: ", add_member: "Añadir miembro", remove: "Quitar", add_user: "Crear usuario", users: "Usuarios", display_name: "Nombre mostrado", is_admin: "Admin", allow_any_scorer_edit: "Cualquier scorer puede editar penalizaciones", open: "Abrir", back: "Atrás", change_password: "Cambiar contraseña", new_password: "Nueva contraseña", csv_paste: "Pegar CSV (n.º,apellido,nombre,país,globo)", no_pilots: "Sin pilotos", no_penalties: "Sin penalizaciones", no_members: "Sin miembros", no_competitions: "Sin competiciones", select_pilot: "Elegir piloto", rule_number_short: "N.º regla", transferred_only: "Solo no transferidas", showing_n_of_m: "{n} de {m}", online: "En línea", offline: "Sin conexión", forbidden: "No permitido", save_settings: "Guardar ajustes", saved: "Guardado", yes: "Sí", no: "No", }, pt: { login_title: "Entrar", username: "Utilizador", password: "Palavra-passe", sign_in: "Entrar", invalid_credentials: "Utilizador ou palavra-passe inválidos", logout: "Sair", settings: "Definições", language: "Idioma", competitions: "Competições", new_competition: "Nova competição", competition_name: "Nome", create: "Criar", cancel: "Cancelar", role: "Papel", system_admin: "Administrador", chief_scorer: "Chief-Scorer", scorer: "Scorer", pilots: "Pilotos", penalties: "Penalizações", members: "Membros", rules: "Regras", settings_tab: "Definições", number: "Número", last_name: "Apelido", first_name: "Nome", country: "País", balloon_id: "Matrícula balão", add_pilot: "Adicionar piloto", import_csv: "Importar CSV", export_csv: "Exportar CSV", flight: "Voo", date: "Data", pilot_number: "N.º piloto", pilot_name: "Nome do piloto", rule: "Regra", task: "Tarefa", penalty_values: "Penalizações", description: "Descrição", created_by: "Criado por", transferred: "Transferido", actions: "Ações", add_penalty: "Adicionar penalização", edit: "Editar", delete: "Eliminar", save: "Guardar", confirm_delete: "Eliminar este registo?", search_rule: "Procurar regra por número ou texto", suggested_penalty: "Penalização sugerida", escalation: "Comportamento em caso de repetição", escalation_same: "Sem alteração", escalation_doubled: "Duplicada de cada vez", escalation_escalate: "Escala: ", add_member: "Adicionar membro", remove: "Remover", add_user: "Criar utilizador", users: "Utilizadores", display_name: "Nome mostrado", is_admin: "Admin", allow_any_scorer_edit: "Qualquer scorer pode editar penalizações", open: "Abrir", back: "Voltar", change_password: "Alterar palavra-passe", new_password: "Nova palavra-passe", csv_paste: "Colar CSV (n.º,apelido,nome,país,balão)", no_pilots: "Sem pilotos", no_penalties: "Sem penalizações", no_members: "Sem membros", no_competitions: "Sem competições", select_pilot: "Escolher piloto", rule_number_short: "N.º regra", transferred_only: "Apenas não transferidas", showing_n_of_m: "{n} de {m}", online: "Online", offline: "Offline", forbidden: "Não autorizado", save_settings: "Guardar definições", saved: "Guardado", yes: "Sim", no: "Não", }, }; let CURRENT_LANG = "en"; function setLang(lang) { if (!I18N_DATA[lang]) lang = "en"; CURRENT_LANG = lang; document.documentElement.lang = lang; } function t(key, vars) { const d = I18N_DATA[CURRENT_LANG] || I18N_DATA.en; let str = d[key] || I18N_DATA.en[key] || key; if (vars) { for (const k in vars) { str = str.replaceAll("{" + k + "}", vars[k]); } } return str; }