Add Translations layer (#2288)

* Add rough translations support into CTFd
* Add `flask-babel` dependency
* Adds language column to users table
* Closes #570 

---------

Co-authored-by: Miłosz Skaza <milosz.skaza@ctfd.io>
This commit is contained in:
Kevin Chung
2023-06-01 15:24:00 -04:00
committed by GitHub
parent 2474d6000d
commit 635b0940e5
24 changed files with 1660 additions and 108 deletions

View File

@@ -7,6 +7,7 @@ from distutils.version import StrictVersion
import jinja2
from flask import Flask, Request
from flask.helpers import safe_join
from flask_babel import Babel
from flask_migrate import upgrade
from jinja2 import FileSystemLoader
from jinja2.sandbox import SandboxedEnvironment
@@ -28,6 +29,7 @@ from CTFd.utils.initialization import (
from CTFd.utils.migrations import create_database, migrations, stamp_latest_revision
from CTFd.utils.sessions import CachingSessionInterface
from CTFd.utils.updates import update_check
from CTFd.utils.user import get_locale
__version__ = "3.5.2"
__channel__ = "oss"
@@ -208,6 +210,10 @@ def create_app(config="CTFd.config.Config"):
# Register Flask-Migrate
migrations.init_app(app, db)
babel = Babel()
babel.locale_selector_func = get_locale
babel.init_app(app)
# Alembic sqlite support is lacking so we should just create_all anyway
if url.drivername.startswith("sqlite"):
# Enable foreign keys for SQLite. This must be before the

View File

@@ -1,7 +1,7 @@
import csv # noqa: I001
import datetime
from io import StringIO
import os
from io import StringIO
from flask import Blueprint, abort
from flask import current_app as app

View File

@@ -0,0 +1,18 @@
from CTFd.constants import RawEnum
class Languages(str, RawEnum):
ENGLISH = "en"
GERMAN = "de"
POLISH = "pl"
LANGUAGE_NAMES = {
"en": "English",
"de": "Deutsch",
"pl": "Polski",
}
SELECT_LANGUAGE_LIST = [("", "")] + [
(str(lang), LANGUAGE_NAMES.get(str(lang))) for lang in Languages
]

View File

@@ -16,6 +16,7 @@ UserAttrs = namedtuple(
"hidden",
"banned",
"verified",
"language",
"team_id",
"created",
],

View File

@@ -1,3 +1,4 @@
from flask_babel import lazy_gettext as _l
from wtforms import PasswordField, StringField
from wtforms.fields.html5 import EmailField
from wtforms.validators import InputRequired
@@ -15,11 +16,11 @@ from CTFd.forms.users import (
def RegistrationForm(*args, **kwargs):
class _RegistrationForm(BaseForm):
name = StringField(
"User Name", validators=[InputRequired()], render_kw={"autofocus": True}
_l("User Name"), validators=[InputRequired()], render_kw={"autofocus": True}
)
email = EmailField("Email", validators=[InputRequired()])
password = PasswordField("Password", validators=[InputRequired()])
submit = SubmitField("Submit")
email = EmailField(_l("Email"), validators=[InputRequired()])
password = PasswordField(_l("Password"), validators=[InputRequired()])
submit = SubmitField(_l("Submit"))
@property
def extra(self):
@@ -35,27 +36,27 @@ def RegistrationForm(*args, **kwargs):
class LoginForm(BaseForm):
name = StringField(
"User Name or Email",
_l("User Name or Email"),
validators=[InputRequired()],
render_kw={"autofocus": True},
)
password = PasswordField("Password", validators=[InputRequired()])
submit = SubmitField("Submit")
password = PasswordField(_l("Password"), validators=[InputRequired()])
submit = SubmitField(_l("Submit"))
class ConfirmForm(BaseForm):
submit = SubmitField("Resend Confirmation Email")
submit = SubmitField(_l("Resend Confirmation Email"))
class ResetPasswordRequestForm(BaseForm):
email = EmailField(
"Email", validators=[InputRequired()], render_kw={"autofocus": True}
_l("Email"), validators=[InputRequired()], render_kw={"autofocus": True}
)
submit = SubmitField("Submit")
submit = SubmitField(_l("Submit"))
class ResetPasswordForm(BaseForm):
password = PasswordField(
"Password", validators=[InputRequired()], render_kw={"autofocus": True}
_l("Password"), validators=[InputRequired()], render_kw={"autofocus": True}
)
submit = SubmitField("Submit")
submit = SubmitField(_l("Submit"))

View File

@@ -1,7 +1,9 @@
from flask import session
from flask_babel import lazy_gettext as _l
from wtforms import PasswordField, SelectField, StringField
from wtforms.fields.html5 import DateField, URLField
from CTFd.constants.languages import SELECT_LANGUAGE_LIST
from CTFd.forms import BaseForm
from CTFd.forms.fields import SubmitField
from CTFd.forms.users import attach_custom_user_fields, build_custom_user_fields
@@ -11,14 +13,15 @@ from CTFd.utils.user import get_current_user
def SettingsForm(*args, **kwargs):
class _SettingsForm(BaseForm):
name = StringField("User Name")
email = StringField("Email")
password = PasswordField("Password")
confirm = PasswordField("Current Password")
affiliation = StringField("Affiliation")
website = URLField("Website")
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)
submit = SubmitField("Submit")
name = StringField(_l("User Name"))
email = StringField(_l("Email"))
language = SelectField(_l("Language"), choices=SELECT_LANGUAGE_LIST)
password = PasswordField(_l("Password"))
confirm = PasswordField(_l("Current Password"))
affiliation = StringField(_l("Affiliation"))
website = URLField(_l("Website"))
country = SelectField(_l("Country"), choices=SELECT_COUNTRIES_LIST)
submit = SubmitField(_l("Submit"))
@property
def extra(self):
@@ -46,5 +49,5 @@ def SettingsForm(*args, **kwargs):
class TokensForm(BaseForm):
expiration = DateField("Expiration")
submit = SubmitField("Generate")
expiration = DateField(_l("Expiration"))
submit = SubmitField(_l("Generate"))

View File

@@ -1,3 +1,4 @@
from flask_babel import lazy_gettext as _l
from wtforms import (
FileField,
HiddenField,
@@ -18,62 +19,72 @@ from CTFd.utils.config import get_themes
class SetupForm(BaseForm):
ctf_name = StringField(
"Event Name", description="The name of your CTF event/workshop"
_l("Event Name"), description=_l("The name of your CTF event/workshop")
)
ctf_description = TextAreaField(
"Event Description", description="Description for the CTF"
_l("Event Description"), description=_l("Description for the CTF")
)
user_mode = RadioField(
"User Mode",
choices=[("teams", "Team Mode"), ("users", "User Mode")],
_l("User Mode"),
choices=[("teams", _l("Team Mode")), ("users", _l("User Mode"))],
default="teams",
description="Controls whether users join together in teams to play (Team Mode) or play as themselves (User Mode)",
description=_l(
"Controls whether users join together in teams to play (Team Mode) or play as themselves (User Mode)"
),
validators=[InputRequired()],
)
name = StringField(
"Admin Username",
description="Your username for the administration account",
_l("Admin Username"),
description=_l("Your username for the administration account"),
validators=[InputRequired()],
)
email = EmailField(
"Admin Email",
description="Your email address for the administration account",
_l("Admin Email"),
description=_l("Your email address for the administration account"),
validators=[InputRequired()],
)
password = PasswordField(
"Admin Password",
description="Your password for the administration account",
_l("Admin Password"),
description=_l("Your password for the administration account"),
validators=[InputRequired()],
)
ctf_logo = FileField(
"Logo",
description="Logo to use for the website instead of a CTF name. Used as the home page button. Optional.",
_l("Logo"),
description=_l(
"Logo to use for the website instead of a CTF name. Used as the home page button. Optional."
),
)
ctf_banner = FileField(
"Banner", description="Banner to use for the homepage. Optional."
_l("Banner"), description=_l("Banner to use for the homepage. Optional.")
)
ctf_small_icon = FileField(
"Small Icon",
description="favicon used in user's browsers. Only PNGs accepted. Must be 32x32px. Optional.",
_l("Small Icon"),
description=_l(
"favicon used in user's browsers. Only PNGs accepted. Must be 32x32px. Optional."
),
)
ctf_theme = SelectField(
"Theme",
description="CTFd Theme to use. Can be changed later.",
_l("Theme"),
description=_l("CTFd Theme to use. Can be changed later."),
choices=list(zip(get_themes(), get_themes())),
default=DEFAULT_THEME,
validators=[InputRequired()],
)
theme_color = HiddenField(
"Theme Color",
description="Color used by theme to control aesthetics. Requires theme support. Optional.",
_l("Theme Color"),
description=_l(
"Color used by theme to control aesthetics. Requires theme support. Optional."
),
)
start = StringField(
"Start Time", description="Time when your CTF is scheduled to start. Optional."
_l("Start Time"),
description=_l("Time when your CTF is scheduled to start. Optional."),
)
end = StringField(
"End Time", description="Time when your CTF is scheduled to end. Optional."
_l("End Time"),
description=_l("Time when your CTF is scheduled to end. Optional."),
)
submit = SubmitField("Finish")
submit = SubmitField(_l("Finish"))

View File

@@ -1,3 +1,4 @@
from flask_babel import lazy_gettext as _l
from wtforms import BooleanField, PasswordField, SelectField, StringField
from wtforms.fields.html5 import EmailField, URLField
from wtforms.validators import InputRequired
@@ -72,16 +73,16 @@ def attach_custom_team_fields(form_cls, **kwargs):
class TeamJoinForm(BaseForm):
name = StringField("Team Name", validators=[InputRequired()])
password = PasswordField("Team Password", validators=[InputRequired()])
submit = SubmitField("Join")
name = StringField(_l("Team Name"), validators=[InputRequired()])
password = PasswordField(_l("Team Password"), validators=[InputRequired()])
submit = SubmitField(_l("Join"))
def TeamRegisterForm(*args, **kwargs):
class _TeamRegisterForm(BaseForm):
name = StringField("Team Name", validators=[InputRequired()])
password = PasswordField("Team Password", validators=[InputRequired()])
submit = SubmitField("Create")
name = StringField(_l("Team Name"), validators=[InputRequired()])
password = PasswordField(_l("Team Password"), validators=[InputRequired()])
submit = SubmitField(_l("Create"))
@property
def extra(self):
@@ -96,30 +97,34 @@ def TeamRegisterForm(*args, **kwargs):
def TeamSettingsForm(*args, **kwargs):
class _TeamSettingsForm(BaseForm):
name = StringField(
"Team Name",
description="Your team's public name shown to other competitors",
_l("Team Name"),
description=_l("Your team's public name shown to other competitors"),
)
password = PasswordField(
"New Team Password", description="Set a new team join password"
_l("New Team Password"), description=_l("Set a new team join password")
)
confirm = PasswordField(
"Confirm Current Team Password",
description="Provide your current team password (or your password) to update your team's password",
_l("Confirm Current Team Password"),
description=_l(
"Provide your current team password (or your password) to update your team's password"
),
)
affiliation = StringField(
"Affiliation",
description="Your team's affiliation publicly shown to other competitors",
_l("Affiliation"),
description=_l(
"Your team's affiliation publicly shown to other competitors"
),
)
website = URLField(
"Website",
description="Your team's website publicly shown to other competitors",
_l("Website"),
description=_l("Your team's website publicly shown to other competitors"),
)
country = SelectField(
"Country",
_l("Country"),
choices=SELECT_COUNTRIES_LIST,
description="Your team's country publicly shown to other competitors",
description=_l("Your team's country publicly shown to other competitors"),
)
submit = SubmitField("Submit")
submit = SubmitField(_l("Submit"))
@property
def extra(self):
@@ -156,7 +161,9 @@ def TeamSettingsForm(*args, **kwargs):
class TeamCaptainForm(BaseForm):
# Choices are populated dynamically at form creation time
captain_id = SelectField("Team Captain", choices=[], validators=[InputRequired()])
captain_id = SelectField(
_l("Team Captain"), choices=[], validators=[InputRequired()]
)
submit = SubmitField("Submit")
@@ -178,29 +185,29 @@ class TeamSearchForm(BaseForm):
class PublicTeamSearchForm(BaseForm):
field = SelectField(
"Search Field",
_l("Search Field"),
choices=[
("name", "Name"),
("affiliation", "Affiliation"),
("website", "Website"),
("name", _l("Name")),
("affiliation", _l("Affiliation")),
("website", _l("Website")),
],
default="name",
validators=[InputRequired()],
)
q = StringField("Parameter", validators=[InputRequired()])
submit = SubmitField("Search")
q = StringField(_l("Parameter"), validators=[InputRequired()])
submit = SubmitField(_l("Search"))
class TeamBaseForm(BaseForm):
name = StringField("Team Name", validators=[InputRequired()])
email = EmailField("Email")
password = PasswordField("Password")
website = URLField("Website")
affiliation = StringField("Affiliation")
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)
hidden = BooleanField("Hidden")
banned = BooleanField("Banned")
submit = SubmitField("Submit")
name = StringField(_l("Team Name"), validators=[InputRequired()])
email = EmailField(_l("Email"))
password = PasswordField(_l("Password"))
website = URLField(_l("Website"))
affiliation = StringField(_l("Affiliation"))
country = SelectField(_l("Country"), choices=SELECT_COUNTRIES_LIST)
hidden = BooleanField(_l("Hidden"))
banned = BooleanField(_l("Banned"))
submit = SubmitField(_l("Submit"))
def TeamCreateForm(*args, **kwargs):
@@ -244,8 +251,8 @@ def TeamEditForm(*args, **kwargs):
class TeamInviteForm(BaseForm):
link = URLField("Invite Link")
link = URLField(_l("Invite Link"))
class TeamInviteJoinForm(BaseForm):
submit = SubmitField("Join")
submit = SubmitField(_l("Join"))

View File

@@ -1,8 +1,10 @@
from flask_babel import lazy_gettext as _l
from wtforms import BooleanField, PasswordField, SelectField, StringField
from wtforms.fields.html5 import EmailField
from wtforms.validators import InputRequired
from CTFd.constants.config import Configs
from CTFd.constants.languages import SELECT_LANGUAGE_LIST
from CTFd.forms import BaseForm
from CTFd.forms.fields import SubmitField
from CTFd.models import UserFieldEntries, UserFields
@@ -130,22 +132,27 @@ class UserSearchForm(BaseForm):
class PublicUserSearchForm(BaseForm):
field = SelectField(
"Search Field",
_l("Search Field"),
choices=[
("name", "Name"),
("affiliation", "Affiliation"),
("website", "Website"),
("name", _l("Name")),
("affiliation", _l("Affiliation")),
("website", _l("Website")),
],
default="name",
validators=[InputRequired()],
)
q = StringField("Parameter", validators=[InputRequired()])
submit = SubmitField("Search")
q = StringField(
_l("Parameter"),
description=_l("Search for matching users"),
validators=[InputRequired()],
)
submit = SubmitField(_l("Search"))
class UserBaseForm(BaseForm):
name = StringField("User Name", validators=[InputRequired()])
email = EmailField("Email", validators=[InputRequired()])
language = SelectField(_l("Language"), choices=SELECT_LANGUAGE_LIST)
password = PasswordField("Password")
website = StringField("Website")
affiliation = StringField("Affiliation")

View File

@@ -336,6 +336,7 @@ class Users(db.Model):
hidden = db.Column(db.Boolean, default=False)
banned = db.Column(db.Boolean, default=False)
verified = db.Column(db.Boolean, default=False)
language = db.Column(db.String(32), nullable=True, default=None)
# Relationship for Teams
team_id = db.Column(db.Integer, db.ForeignKey("teams.id"))

View File

@@ -9,7 +9,7 @@ from CTFd.utils import get_config, string_types
from CTFd.utils.crypto import verify_password
from CTFd.utils.email import check_email_is_whitelisted
from CTFd.utils.user import get_current_user, is_admin
from CTFd.utils.validators import validate_country_code
from CTFd.utils.validators import validate_country_code, validate_language
class UserSchema(ma.ModelSchema):
@@ -50,6 +50,7 @@ class UserSchema(ma.ModelSchema):
else True
],
)
language = field_for(Users, "language", validate=[validate_language])
country = field_for(Users, "country", validate=[validate_country_code])
password = field_for(Users, "password", required=True, allow_none=False)
fields = Nested(
@@ -323,6 +324,7 @@ class UserSchema(ma.ModelSchema):
"website",
"name",
"email",
"language",
"country",
"affiliation",
"bracket",
@@ -339,6 +341,7 @@ class UserSchema(ma.ModelSchema):
"country",
"banned",
"email",
"language",
"affiliation",
"secret",
"bracket",

View File

@@ -9,6 +9,10 @@
{{ form.email.label }}
{{ form.email(class="form-control", autocomplete="off") }}
</div>
<div class="form-group">
{{ form.language.label }}
{{ form.language(class="form-control custom-select", autocomplete="off") }}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control", autocomplete="off") }}

View File

@@ -0,0 +1,732 @@
# German translations for PROJECT.
# Copyright (C) 2023 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-05-17 01:38-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
"Language-Team: de <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.12.1\n"
#: CTFd/forms/auth.py:19 CTFd/forms/self.py:16
#, fuzzy
msgid "User Name"
msgstr "Benutzer"
#: CTFd/forms/auth.py:21 CTFd/forms/auth.py:53 CTFd/forms/self.py:17
#: CTFd/forms/teams.py:203
msgid "Email"
msgstr "E-Mail"
#: CTFd/forms/auth.py:22 CTFd/forms/auth.py:43 CTFd/forms/auth.py:60
#: CTFd/forms/self.py:19 CTFd/forms/teams.py:204
#, fuzzy
msgid "Password"
msgstr "Passwort zurücksetzen"
#: CTFd/forms/auth.py:23 CTFd/forms/auth.py:44 CTFd/forms/auth.py:55
#: CTFd/forms/auth.py:62 CTFd/forms/self.py:24 CTFd/forms/teams.py:127
#: CTFd/forms/teams.py:210 CTFd/themes/core-beta/templates/challenge.html:147
msgid "Submit"
msgstr "Einreichen"
#: CTFd/forms/auth.py:39
#, fuzzy
msgid "User Name or Email"
msgstr "Ihr Benutzername auf der Website"
#: CTFd/forms/auth.py:48
msgid "Resend Confirmation Email"
msgstr "Bestätigungsmail erneut senden"
#: CTFd/forms/self.py:18
msgid "Language"
msgstr ""
#: CTFd/forms/self.py:20
#, fuzzy
msgid "Current Password"
msgstr "Passwort zurücksetzen"
#: CTFd/forms/self.py:21 CTFd/forms/teams.py:113 CTFd/forms/teams.py:191
#: CTFd/forms/teams.py:206 CTFd/forms/users.py:137
#: CTFd/themes/core-beta/templates/teams/teams.html:51
#: CTFd/themes/core-beta/templates/users/users.html:48
msgid "Affiliation"
msgstr "Zugehörigkeit"
#: CTFd/forms/self.py:22 CTFd/forms/teams.py:119 CTFd/forms/teams.py:192
#: CTFd/forms/teams.py:205 CTFd/forms/users.py:138
#: CTFd/themes/core-beta/templates/teams/teams.html:50
#: CTFd/themes/core-beta/templates/users/users.html:47
msgid "Website"
msgstr "Webseite"
#: CTFd/forms/self.py:23 CTFd/forms/teams.py:123 CTFd/forms/teams.py:207
#: CTFd/themes/core-beta/templates/teams/teams.html:52
#: CTFd/themes/core-beta/templates/users/users.html:49
msgid "Country"
msgstr "Land"
#: CTFd/forms/self.py:52 CTFd/themes/core-beta/templates/settings.html:220
msgid "Expiration"
msgstr "Ablauf"
#: CTFd/forms/self.py:53
#, fuzzy
msgid "Generate"
msgstr "Allgemein"
#: CTFd/forms/setup.py:22
#, fuzzy
msgid "Event Name"
msgstr "Benutzer"
#: CTFd/forms/setup.py:22
msgid "The name of your CTF event/workshop"
msgstr "Der Name Ihrer CTF-Veranstaltung/Ihres Workshops"
#: CTFd/forms/setup.py:25
msgid "Event Description"
msgstr "Ereignis Beschreibung"
#: CTFd/forms/setup.py:25
msgid "Description for the CTF"
msgstr "Beschreibung für die CTF"
#: CTFd/forms/setup.py:28 CTFd/forms/setup.py:29
#, fuzzy
msgid "User Mode"
msgstr "Benutzer"
#: CTFd/forms/setup.py:29
#, fuzzy
msgid "Team Mode"
msgstr "Mannschaft"
#: CTFd/forms/setup.py:31
msgid ""
"Controls whether users join together in teams to play (Team Mode) or play"
" as themselves (User Mode)"
msgstr ""
"Legt fest, ob sich die Benutzer in Teams zusammenschließen (Team-Modus) "
"oder alleine spielen (Benutzer-Modus)"
#: CTFd/forms/setup.py:38
#, fuzzy
msgid "Admin Username"
msgstr "Benutzer"
#: CTFd/forms/setup.py:39
#, fuzzy
msgid "Your username for the administration account"
msgstr "Ihr Benutzername auf der Website"
#: CTFd/forms/setup.py:43
#, fuzzy
msgid "Admin Email"
msgstr "Administrationsmenü"
#: CTFd/forms/setup.py:44
msgid "Your email address for the administration account"
msgstr "Ihre E-Mail-Adresse für das Administrationskonto"
#: CTFd/forms/setup.py:48
#, fuzzy
msgid "Admin Password"
msgstr "Passwort zurücksetzen"
#: CTFd/forms/setup.py:49
msgid "Your password for the administration account"
msgstr "Ihr Passwort für das Administrationskonto"
#: CTFd/forms/setup.py:54
#, fuzzy
msgid "Logo"
msgstr "Ausloggen"
#: CTFd/forms/setup.py:55
msgid ""
"Logo to use for the website instead of a CTF name. Used as the home page "
"button. Optional."
msgstr ""
"Logo zur Verwendung für die Website anstelle des CTF-Namens. Wird als "
"Schaltfläche auf der Startseite verwendet. Optional."
#: CTFd/forms/setup.py:60
#, fuzzy
msgid "Banner"
msgstr "Verboten"
#: CTFd/forms/setup.py:60
msgid "Banner to use for the homepage. Optional."
msgstr "Banner, das für die Homepage verwendet werden soll. Optional."
#: CTFd/forms/setup.py:63
msgid "Small Icon"
msgstr "Kleine Ikone"
#: CTFd/forms/setup.py:64
msgid ""
"favicon used in user's browsers. Only PNGs accepted. Must be 32x32px. "
"Optional."
msgstr ""
"Favicon, das in den Browsern der Benutzer verwendet wird. Nur PNGs werden"
" akzeptiert. Muss 32x32px groß sein. Optional."
#: CTFd/forms/setup.py:69
#, fuzzy
msgid "Theme"
msgstr "Zeit"
#: CTFd/forms/setup.py:70
msgid "CTFd Theme to use. Can be changed later."
msgstr "CTFd Theme zu verwenden. Kann später geändert werden."
#: CTFd/forms/setup.py:76
msgid "Theme Color"
msgstr "Thema Farbe"
#: CTFd/forms/setup.py:77
msgid ""
"Color used by theme to control aesthetics. Requires theme support. "
"Optional."
msgstr ""
"Vom Thema verwendete Farbe zur Steuerung der Ästhetik. Erfordert Theme-"
"Unterstützung. Optional."
#: CTFd/forms/setup.py:83
msgid "Start Time"
msgstr "Startzeit"
#: CTFd/forms/setup.py:84
msgid "Time when your CTF is scheduled to start. Optional."
msgstr "Uhrzeit, zu der Ihre CTF beginnen soll. Optional."
#: CTFd/forms/setup.py:87
#, fuzzy
msgid "End Time"
msgstr "Zeit"
#: CTFd/forms/setup.py:88
msgid "Time when your CTF is scheduled to end. Optional."
msgstr "Uhrzeit, zu der die CTF enden soll. Optional."
#: CTFd/forms/setup.py:90
msgid "Finish"
msgstr "Oberfläche"
#: CTFd/forms/teams.py:76 CTFd/forms/teams.py:83 CTFd/forms/teams.py:100
#: CTFd/forms/teams.py:202
#, fuzzy
msgid "Team Name"
msgstr "Mannschaft"
#: CTFd/forms/teams.py:77 CTFd/forms/teams.py:84
#, fuzzy
msgid "Team Password"
msgstr "Passwort zurücksetzen"
#: CTFd/forms/teams.py:78 CTFd/forms/teams.py:258
#, fuzzy
msgid "Join"
msgstr "Anmeldung"
#: CTFd/forms/teams.py:85
#, fuzzy
msgid "Create"
msgstr "Erstellt"
#: CTFd/forms/teams.py:101
msgid "Your team's public name shown to other competitors"
msgstr "Der öffentliche Name Ihres Teams wird für andere Teilnehmer angezeigt"
#: CTFd/forms/teams.py:104
#, fuzzy
msgid "New Team Password"
msgstr "Passwort zurücksetzen"
#: CTFd/forms/teams.py:104
msgid "Set a new team join password"
msgstr "Ein neues Passwort für den Beitritt zu einem Team festlegen"
#: CTFd/forms/teams.py:107
#, fuzzy
msgid "Confirm Current Team Password"
msgstr "Passwort zurücksetzen"
#: CTFd/forms/teams.py:108
msgid ""
"Provide your current team password (or your password) to update your "
"team's password"
msgstr ""
"Geben Sie Ihr aktuelles Team-Passwort (oder Ihr Passwort) ein, um das "
"Passwort Ihres Teams zu aktualisieren"
#: CTFd/forms/teams.py:114
msgid "Your team's affiliation publicly shown to other competitors"
msgstr ""
"Die Zugehörigkeit Ihres Teams wird für andere Wettbewerber öffentlich "
"angezeigt"
#: CTFd/forms/teams.py:120
msgid "Your team's website publicly shown to other competitors"
msgstr "Die Website Ihres Teams wird öffentlich für andere Wettbewerber angezeigt"
#: CTFd/forms/teams.py:125
msgid "Your team's country publicly shown to other competitors"
msgstr "Das Land Ihres Teams wird öffentlich für andere Teilnehmer angezeigt"
#: CTFd/forms/teams.py:165
msgid "Team Captain"
msgstr "Mannschaftskapitän"
#: CTFd/forms/teams.py:188 CTFd/forms/users.py:134
msgid "Search Field"
msgstr "Suchfeld"
#: CTFd/forms/teams.py:190 CTFd/forms/users.py:136
#: CTFd/themes/core-beta/templates/challenge.html:183
msgid "Name"
msgstr "Name"
#: CTFd/forms/teams.py:197 CTFd/forms/users.py:144
msgid "Parameter"
msgstr "Parameter"
#: CTFd/forms/teams.py:198 CTFd/forms/users.py:148
#, fuzzy
msgid "Search"
msgstr "Benutzer"
#: CTFd/forms/teams.py:208
#, fuzzy
msgid "Hidden"
msgstr "Verboten"
#: CTFd/forms/teams.py:209
msgid "Banned"
msgstr "Verboten"
#: CTFd/forms/teams.py:254
#, fuzzy
msgid "Invite Link"
msgstr "Tipp ansehen"
#: CTFd/forms/users.py:145
msgid "Search for matching users"
msgstr "Suche nach passenden Benutzern"
#: CTFd/themes/core-beta/templates/base.html:49
msgid "Powered by CTFd"
msgstr ""
#: CTFd/themes/core-beta/templates/challenge.html:11
#: CTFd/themes/core-beta/templates/users/private.html:114
#: CTFd/themes/core-beta/templates/users/public.html:113
msgid "Challenge"
msgstr ""
#: CTFd/themes/core-beta/templates/challenge.html:19
#, fuzzy, python-format
msgid "%(num)d Solve"
msgid_plural "%(num)d Solves"
msgstr[0] ""
msgstr[1] ""
#: CTFd/themes/core-beta/templates/challenge.html:73
msgid "View Hint"
msgstr ""
#: CTFd/themes/core-beta/templates/challenge.html:135
msgid "Flag"
msgstr ""
#: CTFd/themes/core-beta/templates/challenge.html:167
msgid "Next Challenge"
msgstr ""
#: CTFd/themes/core-beta/templates/challenge.html:186
#: CTFd/themes/core-beta/templates/setup.html:237
#: CTFd/themes/core-beta/templates/setup.html:258
#, fuzzy
msgid "Date"
msgstr "Erstellt"
#: CTFd/themes/core-beta/templates/challenges.html:7
#: CTFd/themes/core-beta/templates/components/navbar.html:57
msgid "Challenges"
msgstr ""
#: CTFd/themes/core-beta/templates/confirm.html:7
msgid "Confirm"
msgstr ""
#: CTFd/themes/core-beta/templates/confirm.html:18
msgid "We've sent a confirmation email to your email address."
msgstr ""
#: CTFd/themes/core-beta/templates/confirm.html:24
msgid "Please click the link in that email to confirm your account."
msgstr ""
#: CTFd/themes/core-beta/templates/confirm.html:30
msgid ""
"If the email doesnt arrive, check your spam folder or contact an "
"administrator to manually verify your account."
msgstr ""
#: CTFd/themes/core-beta/templates/confirm.html:43
msgid "Change Email Address"
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:178
#: CTFd/themes/core-beta/templates/login.html:7
#, fuzzy
msgid "Login"
msgstr "Ausloggen"
#: CTFd/themes/core-beta/templates/login.html:40
msgid "Forgot your password?"
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:97
#: CTFd/themes/core-beta/templates/notifications.html:7
msgid "Notifications"
msgstr ""
#: CTFd/themes/core-beta/templates/notifications.html:14
msgid "There are no notifications yet"
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:165
#: CTFd/themes/core-beta/templates/register.html:7
msgid "Register"
msgstr ""
#: CTFd/themes/core-beta/templates/register.html:35
#, fuzzy
msgid "Your username on the site"
msgstr "Ihr Benutzername auf der Website"
#: CTFd/themes/core-beta/templates/register.html:43
msgid "Never shown to the public"
msgstr ""
#: CTFd/themes/core-beta/templates/register.html:51
msgid "Password used to log into your account"
msgstr ""
#: CTFd/themes/core-beta/templates/reset_password.html:7
#, fuzzy
msgid "Reset Password"
msgstr "Passwort zurücksetzen"
#: CTFd/themes/core-beta/templates/reset_password.html:21
msgid ""
"You can now reset the password for your account and log in. Please enter "
"in a new password below."
msgstr ""
#: CTFd/themes/core-beta/templates/reset_password.html:44
msgid "Please provide the email address associated with your account below."
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:135
#: CTFd/themes/core-beta/templates/settings.html:8
msgid "Settings"
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:123
#: CTFd/themes/core-beta/templates/settings.html:21
msgid "Profile"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:26
msgid "Access Tokens"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:95
msgid "Your profile has been updated"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:103
msgid "Error:"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:129
#, fuzzy
msgid "API Key Generated"
msgstr "Allgemein"
#: CTFd/themes/core-beta/templates/settings.html:137
msgid "Please copy your API Key, it won't be shown again!"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:178
msgid "Active Tokens"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:190
msgid "Delete Token"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:199
msgid "Are you sure you want to delete this token?"
msgstr ""
#: CTFd/themes/core-beta/templates/settings.html:219
#, fuzzy
msgid "Created"
msgstr "Erstellt"
#: CTFd/themes/core-beta/templates/settings.html:221
msgid "Delete"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:24
msgid "Setup"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:44
#, fuzzy
msgid "General"
msgstr "Allgemein"
#: CTFd/themes/core-beta/templates/setup.html:47
#, fuzzy
msgid "Mode"
msgstr "Benutzer"
#: CTFd/themes/core-beta/templates/setup.html:50
#, fuzzy
msgid "Administration"
msgstr "Zugehörigkeit"
#: CTFd/themes/core-beta/templates/setup.html:53
msgid "Style"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:56
msgid "Date &amp; Time"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:59
#, fuzzy
msgid "Integrations"
msgstr "Ablauf"
#: CTFd/themes/core-beta/templates/setup.html:108
msgid "Participants register accounts and form teams"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:109
msgid "If a team member solves a challenge, the entire team receives credit"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:111
msgid "Easier to see which team member solved a challenge"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:112
msgid "May be slightly more difficult to administer"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:116
msgid "Participants only register an individual account"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:117
msgid "Players can share accounts to form pseudo-teams"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:119
msgid "Easier to organize"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:120
msgid "Difficult to attribute solutions to individual team members"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:164
msgid "Subscribe email address to the CTFd LLC Newsletter for news and updates"
msgstr ""
#: CTFd/themes/core-beta/templates/setup.html:241
#: CTFd/themes/core-beta/templates/setup.html:262
#: CTFd/themes/core-beta/templates/users/private.html:117
#: CTFd/themes/core-beta/templates/users/public.html:116
#, fuzzy
msgid "Time"
msgstr "Zeit"
#: CTFd/themes/core-beta/templates/setup.html:266
msgid "UTC Preview"
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:34
#: CTFd/themes/core-beta/templates/users/users.html:6
#, fuzzy
msgid "Users"
msgstr "Benutzer"
#: CTFd/themes/core-beta/templates/components/navbar.html:41
#: CTFd/themes/core-beta/templates/teams/teams.html:6
#, fuzzy
msgid "Teams"
msgstr "Mannschaft"
#: CTFd/themes/core-beta/templates/components/navbar.html:50
msgid "Scoreboard"
msgstr ""
#: CTFd/themes/core-beta/templates/components/navbar.html:80
#, fuzzy
msgid "Admin Panel"
msgstr "Administrationsmenü"
#: CTFd/themes/core-beta/templates/components/navbar.html:110
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:6
#: CTFd/themes/core-beta/templates/teams/teams.html:49
#, fuzzy
msgid "Team"
msgstr "Mannschaft"
#: CTFd/themes/core-beta/templates/components/navbar.html:147
#, fuzzy
msgid "Logout"
msgstr "Ausloggen"
#: CTFd/themes/core-beta/templates/errors/403.html:9
#, fuzzy
msgid "Forbidden"
msgstr "Verboten"
#: CTFd/themes/core-beta/templates/errors/404.html:11
msgid "File not found"
msgstr ""
#: CTFd/themes/core-beta/templates/errors/404.html:12
msgid "Sorry about that"
msgstr ""
#: CTFd/themes/core-beta/templates/errors/429.html:11
msgid "Too many requests"
msgstr ""
#: CTFd/themes/core-beta/templates/errors/429.html:12
msgid "Please slow down!"
msgstr ""
#: CTFd/themes/core-beta/templates/errors/502.html:11
msgid "Bad Gateway"
msgstr ""
#: CTFd/themes/core-beta/templates/macros/forms.html:13
#: CTFd/themes/core-beta/templates/macros/forms.html:36
msgid "(Optional)"
msgstr ""
#: CTFd/themes/core-beta/templates/teams/invite.html:6
#: CTFd/themes/core-beta/templates/teams/join_team.html:6
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:36
#, fuzzy
msgid "Join Team"
msgstr "Mannschaft"
#: CTFd/themes/core-beta/templates/teams/invite.html:15
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:12
msgid "Welcome to"
msgstr ""
#: CTFd/themes/core-beta/templates/teams/invite.html:19
msgid "Click the button below to join the team!"
msgstr ""
#: CTFd/themes/core-beta/templates/teams/new_team.html:6
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:39
#, fuzzy
msgid "Create Team"
msgstr "Erstellt"
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:14
msgid "In order to participate you must either join or create a team."
msgstr ""
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:22
msgid "Play with Official Team"
msgstr ""
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:27
msgid "Join Unofficial Team"
msgstr ""
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:30
msgid "Create Unofficial Team"
msgstr ""
#: CTFd/themes/core-beta/templates/teams/teams.html:124
#: CTFd/themes/core-beta/templates/users/users.html:114
msgid "Page"
msgstr ""
#: CTFd/themes/core-beta/templates/users/private.html:21
#: CTFd/themes/core-beta/templates/users/public.html:21
#: CTFd/themes/core-beta/templates/users/users.html:66
msgid "Official"
msgstr ""
#: CTFd/themes/core-beta/templates/users/private.html:88
#: CTFd/themes/core-beta/templates/users/public.html:88
msgid "Awards"
msgstr ""
#: CTFd/themes/core-beta/templates/users/private.html:110
msgid "Solves"
msgstr ""
#: CTFd/themes/core-beta/templates/users/private.html:115
#: CTFd/themes/core-beta/templates/users/public.html:114
msgid "Category"
msgstr ""
#: CTFd/themes/core-beta/templates/users/private.html:116
#: CTFd/themes/core-beta/templates/users/public.html:115
msgid "Value"
msgstr ""
#: CTFd/themes/core-beta/templates/users/private.html:203
#: CTFd/themes/core-beta/templates/users/public.html:204
msgid "No solves yet"
msgstr ""
#: CTFd/themes/core-beta/templates/users/users.html:46
#, fuzzy
msgid "User"
msgstr "Benutzer"
#: CTFd/utils/modes/__init__.py:35
#, fuzzy
msgid "user"
msgid_plural "users"
msgstr[0] "Benutzer"
msgstr[1] ""
#: CTFd/utils/modes/__init__.py:37
#, fuzzy
msgid "team"
msgid_plural "teams"
msgstr[0] "Mannschaft"
msgstr[1] ""

View File

@@ -0,0 +1,689 @@
# Polish translations for PROJECT.
# Copyright (C) 2023 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-05-17 01:38-0400\n"
"PO-Revision-Date: 2023-05-17 01:52-0400\n"
"Last-Translator: \n"
"Language-Team: pl <LL@li.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
"Generated-By: Babel 2.12.1\n"
"X-Generator: Poedit 3.3.1\n"
#: CTFd/forms/auth.py:19 CTFd/forms/self.py:16
msgid "User Name"
msgstr "Nazwa użytkownika"
#: CTFd/forms/auth.py:21 CTFd/forms/auth.py:53 CTFd/forms/self.py:17
#: CTFd/forms/teams.py:203
msgid "Email"
msgstr "Email"
#: CTFd/forms/auth.py:22 CTFd/forms/auth.py:43 CTFd/forms/auth.py:60
#: CTFd/forms/self.py:19 CTFd/forms/teams.py:204
msgid "Password"
msgstr "Hasło"
#: CTFd/forms/auth.py:23 CTFd/forms/auth.py:44 CTFd/forms/auth.py:55
#: CTFd/forms/auth.py:62 CTFd/forms/self.py:24 CTFd/forms/teams.py:127
#: CTFd/forms/teams.py:210 CTFd/themes/core-beta/templates/challenge.html:147
msgid "Submit"
msgstr "Wyślij"
#: CTFd/forms/auth.py:39
msgid "User Name or Email"
msgstr "Nazwa użytkownika lub adres email"
#: CTFd/forms/auth.py:48
msgid "Resend Confirmation Email"
msgstr "Ponownie wyślij email z potwierdzeniem"
#: CTFd/forms/self.py:18
msgid "Language"
msgstr "Język"
#: CTFd/forms/self.py:20
msgid "Current Password"
msgstr "Obecne hasło"
#: CTFd/forms/self.py:21 CTFd/forms/teams.py:113 CTFd/forms/teams.py:191
#: CTFd/forms/teams.py:206 CTFd/forms/users.py:137
#: CTFd/themes/core-beta/templates/teams/teams.html:51
#: CTFd/themes/core-beta/templates/users/users.html:48
msgid "Affiliation"
msgstr "Afiliacja"
#: CTFd/forms/self.py:22 CTFd/forms/teams.py:119 CTFd/forms/teams.py:192
#: CTFd/forms/teams.py:205 CTFd/forms/users.py:138
#: CTFd/themes/core-beta/templates/teams/teams.html:50
#: CTFd/themes/core-beta/templates/users/users.html:47
msgid "Website"
msgstr "Strona internetowa"
#: CTFd/forms/self.py:23 CTFd/forms/teams.py:123 CTFd/forms/teams.py:207
#: CTFd/themes/core-beta/templates/teams/teams.html:52
#: CTFd/themes/core-beta/templates/users/users.html:49
msgid "Country"
msgstr "Państwo"
#: CTFd/forms/self.py:52 CTFd/themes/core-beta/templates/settings.html:220
msgid "Expiration"
msgstr "Data Wygaśnięcia"
#: CTFd/forms/self.py:53
msgid "Generate"
msgstr "Wygeneruj"
#: CTFd/forms/setup.py:22
msgid "Event Name"
msgstr "Nazwa Wydarzenia"
#: CTFd/forms/setup.py:22
msgid "The name of your CTF event/workshop"
msgstr "Nazwa wydarzenia / warsztatów CTF"
#: CTFd/forms/setup.py:25
msgid "Event Description"
msgstr "Opis wydarzenia"
#: CTFd/forms/setup.py:25
msgid "Description for the CTF"
msgstr "Opis CTFa"
#: CTFd/forms/setup.py:28 CTFd/forms/setup.py:29
msgid "User Mode"
msgstr "Tryb indywidualny"
#: CTFd/forms/setup.py:29
msgid "Team Mode"
msgstr "Tryb zespołowy"
#: CTFd/forms/setup.py:31
msgid ""
"Controls whether users join together in teams to play (Team Mode) or play as "
"themselves (User Mode)"
msgstr ""
"Kontroluje czy użytkownicy grają razem w zespołach (Tryb zespołowy) czy "
"samodzielnie (Tryb indywidualny)"
#: CTFd/forms/setup.py:38
msgid "Admin Username"
msgstr "Nazwa administratora"
#: CTFd/forms/setup.py:39
msgid "Your username for the administration account"
msgstr "Twoja nazwa użytkownika dla konta administratora"
#: CTFd/forms/setup.py:43
msgid "Admin Email"
msgstr "Adres email administratora"
#: CTFd/forms/setup.py:44
msgid "Your email address for the administration account"
msgstr "Twój adres email dla konta administratora"
#: CTFd/forms/setup.py:48
msgid "Admin Password"
msgstr "Hasło administratora"
#: CTFd/forms/setup.py:49
msgid "Your password for the administration account"
msgstr "Twoje hasło dla konta administratora"
#: CTFd/forms/setup.py:54
msgid "Logo"
msgstr "Logo"
#: CTFd/forms/setup.py:55
msgid ""
"Logo to use for the website instead of a CTF name. Used as the home page "
"button. Optional."
msgstr ""
"Logo do wyświetlania na stronie, zamiast nazwy CTFa. Funkcjonuje jako "
"przycisk strony głównej. Opcjonalne."
#: CTFd/forms/setup.py:60
msgid "Banner"
msgstr "Baner"
#: CTFd/forms/setup.py:60
msgid "Banner to use for the homepage. Optional."
msgstr "Baner na stronę główną. Opcjonalny."
#: CTFd/forms/setup.py:63
msgid "Small Icon"
msgstr "Mała Ikona"
#: CTFd/forms/setup.py:64
msgid ""
"favicon used in user's browsers. Only PNGs accepted. Must be 32x32px. "
"Optional."
msgstr ""
"favikona używana w przeglądace. Tylko pliki PNG. Musi mieć wymiary 32x32 "
"piksele. Opcjonalna."
#: CTFd/forms/setup.py:69
msgid "Theme"
msgstr "Motyw"
#: CTFd/forms/setup.py:70
msgid "CTFd Theme to use. Can be changed later."
msgstr "Motyw CTFd. Może być później zmieniony."
#: CTFd/forms/setup.py:76
msgid "Theme Color"
msgstr "Kolor motywu"
#: CTFd/forms/setup.py:77
msgid ""
"Color used by theme to control aesthetics. Requires theme support. Optional."
msgstr ""
"Kolor używany przez motyw do zmiany estetyki. Wymaga wsparcia ze strony "
"motywu. Opcjonalny."
#: CTFd/forms/setup.py:83
msgid "Start Time"
msgstr "Czas rozpoczęcia"
#: CTFd/forms/setup.py:84
msgid "Time when your CTF is scheduled to start. Optional."
msgstr "Czas, kiedy CTF ma się rozpocząć. Opcjonalny."
#: CTFd/forms/setup.py:87
msgid "End Time"
msgstr "Czas zakończenia"
#: CTFd/forms/setup.py:88
msgid "Time when your CTF is scheduled to end. Optional."
msgstr "Czas, kiedy CTF ma się rozpocząć. Opcjonalny."
#: CTFd/forms/setup.py:90
msgid "Finish"
msgstr "Zakończ"
#: CTFd/forms/teams.py:76 CTFd/forms/teams.py:83 CTFd/forms/teams.py:100
#: CTFd/forms/teams.py:202
msgid "Team Name"
msgstr "Nazwa zespołu"
#: CTFd/forms/teams.py:77 CTFd/forms/teams.py:84
msgid "Team Password"
msgstr "Hasło zespołu"
#: CTFd/forms/teams.py:78 CTFd/forms/teams.py:258
msgid "Join"
msgstr "Dołącz"
#: CTFd/forms/teams.py:85
msgid "Create"
msgstr "Utwórz"
#: CTFd/forms/teams.py:101
msgid "Your team's public name shown to other competitors"
msgstr "Nazwa Twojejgo zespołu, wyświetlana innym uczestnikom"
#: CTFd/forms/teams.py:104
msgid "New Team Password"
msgstr "Nowe hasło zespołu"
#: CTFd/forms/teams.py:104
msgid "Set a new team join password"
msgstr "Ustaw nowe hasło do dołączenia do zespołu"
#: CTFd/forms/teams.py:107
msgid "Confirm Current Team Password"
msgstr "Potwierdź aktualne hasło zespołu"
#: CTFd/forms/teams.py:108
msgid ""
"Provide your current team password (or your password) to update your team's "
"password"
msgstr "Podaj obecne hasło zespołu (lub swoje hasło) aby zmienić hasło zespołu"
#: CTFd/forms/teams.py:114
msgid "Your team's affiliation publicly shown to other competitors"
msgstr "Afiliacja Twojego zespołu, wyświetlana innym uczestnikom"
#: CTFd/forms/teams.py:120
msgid "Your team's website publicly shown to other competitors"
msgstr "Strona internetowa Twojego zespołu, wyświetlana innym uczestnikom"
#: CTFd/forms/teams.py:125
msgid "Your team's country publicly shown to other competitors"
msgstr "Strona internetowa Twojego zespołu, wyświetlana innym uczestnikom"
#: CTFd/forms/teams.py:165
msgid "Team Captain"
msgstr "Kapitan zespołu"
#: CTFd/forms/teams.py:188 CTFd/forms/users.py:134
msgid "Search Field"
msgstr "Pole wyszukiwania"
#: CTFd/forms/teams.py:190 CTFd/forms/users.py:136
#: CTFd/themes/core-beta/templates/challenge.html:183
msgid "Name"
msgstr "Nazwa"
#: CTFd/forms/teams.py:197 CTFd/forms/users.py:144
msgid "Parameter"
msgstr "Parametr"
#: CTFd/forms/teams.py:198 CTFd/forms/users.py:148
msgid "Search"
msgstr "Szukaj"
#: CTFd/forms/teams.py:208
msgid "Hidden"
msgstr "Ukryty"
#: CTFd/forms/teams.py:209
msgid "Banned"
msgstr "Zablokowany"
#: CTFd/forms/teams.py:254
msgid "Invite Link"
msgstr "Link zaproszenia"
#: CTFd/forms/users.py:145
msgid "Search for matching users"
msgstr "Szukaj użytkownika"
#: CTFd/themes/core-beta/templates/base.html:49
msgid "Powered by CTFd"
msgstr "Obsługiwane przez CTFd"
#: CTFd/themes/core-beta/templates/challenge.html:11
#: CTFd/themes/core-beta/templates/users/private.html:114
#: CTFd/themes/core-beta/templates/users/public.html:113
msgid "Challenge"
msgstr "Wyzwanie"
#: CTFd/themes/core-beta/templates/challenge.html:19
#, python-format
msgid "%(num)d Solve"
msgid_plural "%(num)d Solves"
msgstr[0] "%(num)d Rozwiąż"
msgstr[1] "%(num)d Rozwiązania"
msgstr[2] "%(num)d Rozwiązań"
#: CTFd/themes/core-beta/templates/challenge.html:73
msgid "View Hint"
msgstr "Zobacz podpowiedź"
#: CTFd/themes/core-beta/templates/challenge.html:135
msgid "Flag"
msgstr "Flaga"
#: CTFd/themes/core-beta/templates/challenge.html:167
msgid "Next Challenge"
msgstr "Następne wyzwanie"
#: CTFd/themes/core-beta/templates/challenge.html:186
#: CTFd/themes/core-beta/templates/setup.html:237
#: CTFd/themes/core-beta/templates/setup.html:258
msgid "Date"
msgstr "Data"
#: CTFd/themes/core-beta/templates/challenges.html:7
#: CTFd/themes/core-beta/templates/components/navbar.html:57
msgid "Challenges"
msgstr "Wyzwania"
#: CTFd/themes/core-beta/templates/confirm.html:7
msgid "Confirm"
msgstr "Potwierdzać"
#: CTFd/themes/core-beta/templates/confirm.html:18
msgid "We've sent a confirmation email to your email address."
msgstr "Wysłaliśmy wiadomość e-mail z potwierdzeniem na Twój adres e-mail."
#: CTFd/themes/core-beta/templates/confirm.html:24
msgid "Please click the link in that email to confirm your account."
msgstr "Kliknij link w tym e-mailu, aby potwierdzić swoje konto."
#: CTFd/themes/core-beta/templates/confirm.html:30
msgid ""
"If the email doesnt arrive, check your spam folder or contact an "
"administrator to manually verify your account."
msgstr ""
"Jeśli wiadomość e-mail nie dotrze, sprawdź folder ze spamem lub skontaktuj "
"się z administratorem, aby ręcznie zweryfikować konto."
#: CTFd/themes/core-beta/templates/confirm.html:43
msgid "Change Email Address"
msgstr "Zmień adres email"
#: CTFd/themes/core-beta/templates/components/navbar.html:178
#: CTFd/themes/core-beta/templates/login.html:7
msgid "Login"
msgstr "Zaloguj sie"
#: CTFd/themes/core-beta/templates/login.html:40
msgid "Forgot your password?"
msgstr "Zapomniałeś hasła?"
#: CTFd/themes/core-beta/templates/components/navbar.html:97
#: CTFd/themes/core-beta/templates/notifications.html:7
msgid "Notifications"
msgstr "Powiadomienia"
#: CTFd/themes/core-beta/templates/notifications.html:14
msgid "There are no notifications yet"
msgstr "Nie ma jeszcze żadnych powiadomień"
#: CTFd/themes/core-beta/templates/components/navbar.html:165
#: CTFd/themes/core-beta/templates/register.html:7
msgid "Register"
msgstr "Rejestr"
#: CTFd/themes/core-beta/templates/register.html:35
msgid "Your username on the site"
msgstr "Twoja nazwa użytkownika na stronie"
#: CTFd/themes/core-beta/templates/register.html:43
msgid "Never shown to the public"
msgstr "Nigdy nie pokazany publicznie"
#: CTFd/themes/core-beta/templates/register.html:51
msgid "Password used to log into your account"
msgstr "Hasło używane do logowania się na Twoje konto"
#: CTFd/themes/core-beta/templates/reset_password.html:7
msgid "Reset Password"
msgstr "Zresetuj hasło"
#: CTFd/themes/core-beta/templates/reset_password.html:21
msgid ""
"You can now reset the password for your account and log in. Please enter in "
"a new password below."
msgstr ""
"Możesz teraz zresetować hasło do swojego konta i zalogować się. Wprowadź "
"nowe hasło poniżej."
#: CTFd/themes/core-beta/templates/reset_password.html:44
msgid "Please provide the email address associated with your account below."
msgstr "Podaj adres e-mail powiązany z Twoim kontem poniżej."
#: CTFd/themes/core-beta/templates/components/navbar.html:135
#: CTFd/themes/core-beta/templates/settings.html:8
msgid "Settings"
msgstr "Ustawienia"
#: CTFd/themes/core-beta/templates/components/navbar.html:123
#: CTFd/themes/core-beta/templates/settings.html:21
msgid "Profile"
msgstr "Profil"
#: CTFd/themes/core-beta/templates/settings.html:26
msgid "Access Tokens"
msgstr "Tokeny dostępu"
#: CTFd/themes/core-beta/templates/settings.html:95
msgid "Your profile has been updated"
msgstr "Twój profil został zaktualizowany"
#: CTFd/themes/core-beta/templates/settings.html:103
msgid "Error:"
msgstr "Błąd:"
#: CTFd/themes/core-beta/templates/settings.html:129
msgid "API Key Generated"
msgstr "Wygenerowano klucz API"
#: CTFd/themes/core-beta/templates/settings.html:137
msgid "Please copy your API Key, it won't be shown again!"
msgstr "Skopiuj swój klucz API, nie będzie on już pokazywany!"
#: CTFd/themes/core-beta/templates/settings.html:178
msgid "Active Tokens"
msgstr "Aktywne tokeny"
#: CTFd/themes/core-beta/templates/settings.html:190
msgid "Delete Token"
msgstr "Usuń token"
#: CTFd/themes/core-beta/templates/settings.html:199
msgid "Are you sure you want to delete this token?"
msgstr "Czy na pewno chcesz usunąć ten token?"
#: CTFd/themes/core-beta/templates/settings.html:219
msgid "Created"
msgstr "Utworzony"
#: CTFd/themes/core-beta/templates/settings.html:221
msgid "Delete"
msgstr "Usuwać"
#: CTFd/themes/core-beta/templates/setup.html:24
msgid "Setup"
msgstr "Organizować coś"
#: CTFd/themes/core-beta/templates/setup.html:44
msgid "General"
msgstr "Ogólny"
#: CTFd/themes/core-beta/templates/setup.html:47
msgid "Mode"
msgstr "Tryb"
#: CTFd/themes/core-beta/templates/setup.html:50
msgid "Administration"
msgstr "Administracja"
#: CTFd/themes/core-beta/templates/setup.html:53
msgid "Style"
msgstr "Styl"
#: CTFd/themes/core-beta/templates/setup.html:56
msgid "Date &amp; Time"
msgstr "Data &amp; Czas"
#: CTFd/themes/core-beta/templates/setup.html:59
msgid "Integrations"
msgstr "Integracje"
#: CTFd/themes/core-beta/templates/setup.html:108
msgid "Participants register accounts and form teams"
msgstr "Uczestnicy rejestrują konta i tworzą zespoły"
#: CTFd/themes/core-beta/templates/setup.html:109
msgid "If a team member solves a challenge, the entire team receives credit"
msgstr "Jeśli członek zespołu rozwiąże wyzwanie, cały zespół otrzymuje punkty"
#: CTFd/themes/core-beta/templates/setup.html:111
msgid "Easier to see which team member solved a challenge"
msgstr "Łatwiej zobaczyć, który członek zespołu rozwiązał wyzwanie"
#: CTFd/themes/core-beta/templates/setup.html:112
msgid "May be slightly more difficult to administer"
msgstr "Może być nieco trudniejszy w administrowaniu"
#: CTFd/themes/core-beta/templates/setup.html:116
msgid "Participants only register an individual account"
msgstr "Uczestnicy rejestrują tylko indywidualne konto"
#: CTFd/themes/core-beta/templates/setup.html:117
msgid "Players can share accounts to form pseudo-teams"
msgstr "Gracze mogą dzielić się kontami, tworząc pseudo-zespoły"
#: CTFd/themes/core-beta/templates/setup.html:119
msgid "Easier to organize"
msgstr "Łatwiejsze do zorganizowania"
#: CTFd/themes/core-beta/templates/setup.html:120
msgid "Difficult to attribute solutions to individual team members"
msgstr "Trudno przypisać rozwiązania poszczególnym członkom zespołu"
#: CTFd/themes/core-beta/templates/setup.html:164
msgid "Subscribe email address to the CTFd LLC Newsletter for news and updates"
msgstr ""
"Subskrybuj adres e-mail biuletynu CTFd LLC, aby otrzymywać wiadomości i "
"aktualizacje"
#: CTFd/themes/core-beta/templates/setup.html:241
#: CTFd/themes/core-beta/templates/setup.html:262
#: CTFd/themes/core-beta/templates/users/private.html:117
#: CTFd/themes/core-beta/templates/users/public.html:116
msgid "Time"
msgstr "Czas"
#: CTFd/themes/core-beta/templates/setup.html:266
msgid "UTC Preview"
msgstr "Podgląd czasu UTC"
#: CTFd/themes/core-beta/templates/components/navbar.html:34
#: CTFd/themes/core-beta/templates/users/users.html:6
msgid "Users"
msgstr "Użytkownicy"
#: CTFd/themes/core-beta/templates/components/navbar.html:41
#: CTFd/themes/core-beta/templates/teams/teams.html:6
msgid "Teams"
msgstr "Zespoły"
#: CTFd/themes/core-beta/templates/components/navbar.html:50
msgid "Scoreboard"
msgstr "Tablica wyników"
#: CTFd/themes/core-beta/templates/components/navbar.html:80
msgid "Admin Panel"
msgstr "Panel administratora"
#: CTFd/themes/core-beta/templates/components/navbar.html:110
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:6
#: CTFd/themes/core-beta/templates/teams/teams.html:49
msgid "Team"
msgstr "Zespół"
#: CTFd/themes/core-beta/templates/components/navbar.html:147
msgid "Logout"
msgstr "Wyloguj"
#: CTFd/themes/core-beta/templates/errors/403.html:9
msgid "Forbidden"
msgstr "Zabroniony"
#: CTFd/themes/core-beta/templates/errors/404.html:11
msgid "File not found"
msgstr "Nie znaleziono pliku"
#: CTFd/themes/core-beta/templates/errors/404.html:12
msgid "Sorry about that"
msgstr "Przepraszam za to"
#: CTFd/themes/core-beta/templates/errors/429.html:11
msgid "Too many requests"
msgstr "Zbyt dużo próśb"
#: CTFd/themes/core-beta/templates/errors/429.html:12
msgid "Please slow down!"
msgstr "Proszę zwolnij!"
#: CTFd/themes/core-beta/templates/errors/502.html:11
msgid "Bad Gateway"
msgstr "Zła Brama"
#: CTFd/themes/core-beta/templates/macros/forms.html:13
#: CTFd/themes/core-beta/templates/macros/forms.html:36
msgid "(Optional)"
msgstr "(Opcjonalny)"
#: CTFd/themes/core-beta/templates/teams/invite.html:6
#: CTFd/themes/core-beta/templates/teams/join_team.html:6
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:36
msgid "Join Team"
msgstr "Dołącz do drużyny"
#: CTFd/themes/core-beta/templates/teams/invite.html:15
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:12
msgid "Welcome to"
msgstr "Witamy w"
#: CTFd/themes/core-beta/templates/teams/invite.html:19
msgid "Click the button below to join the team!"
msgstr "Kliknij przycisk poniżej, aby dołączyć do zespołu!"
#: CTFd/themes/core-beta/templates/teams/new_team.html:6
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:39
msgid "Create Team"
msgstr "Utwórz zespół"
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:14
msgid "In order to participate you must either join or create a team."
msgstr "Aby wziąć udział, musisz dołączyć do zespołu lub go utworzyć."
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:22
msgid "Play with Official Team"
msgstr "Graj z oficjalną drużyną"
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:27
msgid "Join Unofficial Team"
msgstr "Dołącz do nieoficjalnego zespołu"
#: CTFd/themes/core-beta/templates/teams/team_enrollment.html:30
msgid "Create Unofficial Team"
msgstr "Utwórz nieoficjalny zespół"
#: CTFd/themes/core-beta/templates/teams/teams.html:124
#: CTFd/themes/core-beta/templates/users/users.html:114
msgid "Page"
msgstr "Strona"
#: CTFd/themes/core-beta/templates/users/private.html:21
#: CTFd/themes/core-beta/templates/users/public.html:21
#: CTFd/themes/core-beta/templates/users/users.html:66
msgid "Official"
msgstr "Urzędnik"
#: CTFd/themes/core-beta/templates/users/private.html:88
#: CTFd/themes/core-beta/templates/users/public.html:88
msgid "Awards"
msgstr "Nagrody"
#: CTFd/themes/core-beta/templates/users/private.html:110
msgid "Solves"
msgstr "Rozwiązuje"
#: CTFd/themes/core-beta/templates/users/private.html:115
#: CTFd/themes/core-beta/templates/users/public.html:114
msgid "Category"
msgstr "Kategoria"
#: CTFd/themes/core-beta/templates/users/private.html:116
#: CTFd/themes/core-beta/templates/users/public.html:115
msgid "Value"
msgstr "Wartość"
#: CTFd/themes/core-beta/templates/users/private.html:203
#: CTFd/themes/core-beta/templates/users/public.html:204
msgid "No solves yet"
msgstr "Brak rozwiązań"
#: CTFd/themes/core-beta/templates/users/users.html:46
msgid "User"
msgstr "Użytkownik"
#: CTFd/utils/modes/__init__.py:35
msgid "user"
msgid_plural "users"
msgstr[0] "użytkownik"
msgstr[1] "użytkowników"
msgstr[2] "użytkowników"
#: CTFd/utils/modes/__init__.py:37
msgid "team"
msgid_plural "teams"
msgstr[0] "zespół"
msgstr[1] "drużyny"
msgstr[2] "drużyn"

View File

@@ -263,6 +263,7 @@ COUNTRIES_LIST = [
COUNTRIES_DICT = OrderedDict(COUNTRIES_LIST)
# List of countries suitable for use in forms
# TODO: CTFd 4.0 Move SELECT_COUNTRIES_LIST into constants
SELECT_COUNTRIES_LIST = [("", "")] + COUNTRIES_LIST

View File

@@ -1,4 +1,5 @@
from flask import url_for
from flask_babel import ngettext
from CTFd.models import Teams, Users
from CTFd.utils import get_config
@@ -29,13 +30,12 @@ def get_model():
def get_mode_as_word(plural=False, capitalize=False):
count = 2 if plural else 1
if get_config("user_mode") == USERS_MODE:
word = "user"
word = ngettext("user", "users", count)
else:
word = "team"
word = ngettext("team", "teams", count)
if plural:
word += "s"
if capitalize:
word = word.title()
return word

View File

@@ -5,7 +5,8 @@ from flask import abort
from flask import current_app as app
from flask import redirect, request, session, url_for
from CTFd.cache import cache
from CTFd.cache import cache, clear_user_session
from CTFd.constants.languages import Languages
from CTFd.constants.teams import TeamAttrs
from CTFd.constants.users import UserAttrs
from CTFd.models import Fails, Teams, Tracking, Users, db
@@ -36,7 +37,11 @@ def get_current_user():
def get_current_user_attrs():
if authed():
return get_user_attrs(user_id=session["id"])
try:
return get_user_attrs(user_id=session["id"])
except TypeError:
clear_user_session(user_id=session["id"])
return get_user_attrs(user_id=session["id"])
else:
return None
@@ -94,7 +99,11 @@ def get_current_team():
def get_current_team_attrs():
if authed():
user = get_user_attrs(user_id=session["id"])
try:
user = get_user_attrs(user_id=session["id"])
except TypeError:
clear_user_session(user_id=session["id"])
user = get_user_attrs(user_id=session["id"])
if user and user.team_id:
return get_team_attrs(team_id=user.team_id)
return None
@@ -167,6 +176,15 @@ def get_ip(req=None):
return remote_addr
def get_locale():
if authed():
user = get_current_user_attrs()
if user.language:
return user.language
languages = Languages.values()
return request.accept_languages.best_match(languages)
def get_current_user_recent_ips():
if authed():
return get_user_recent_ips(user_id=session["id"])

View File

@@ -4,6 +4,7 @@ from urllib.parse import urljoin, urlparse
from flask import request
from marshmallow import ValidationError
from CTFd.constants.languages import LANGUAGE_NAMES
from CTFd.models import Users
from CTFd.utils.countries import lookup_country_code
from CTFd.utils.user import get_current_user, is_admin
@@ -39,3 +40,10 @@ def validate_country_code(country_code):
return
if lookup_country_code(country_code) is None:
raise ValidationError("Invalid Country")
def validate_language(language):
if language.strip() == "":
return
if LANGUAGE_NAMES.get(language) is None:
raise ValidationError("Invalid Language")

View File

@@ -316,11 +316,6 @@ def settings():
errors = get_errors()
user = get_current_user()
name = user.name
email = user.email
website = user.website
affiliation = user.affiliation
country = user.country
if is_teams_mode() and get_current_team() is None:
team_url = url_for("teams.private")
@@ -346,11 +341,12 @@ def settings():
return render_template(
"settings.html",
name=name,
email=email,
website=website,
affiliation=affiliation,
country=country,
name=user.name,
email=user.email,
language=user.language,
website=user.website,
affiliation=user.affiliation,
country=user.country,
tokens=tokens,
prevent_name_change=prevent_name_change,
infos=infos,

View File

@@ -30,3 +30,15 @@ serve:
shell:
python manage.py shell
translations-init:
pybabel init -i messages.pot -d CTFd/translations -l de
translations-extract:
pybabel extract -F babel.cfg -k lazy_gettext -k _l -o messages.pot .
translations-update:
pybabel update --ignore-obsolete -i messages.pot -d CTFd/translations
translations-compile:
pybabel compile -f -d CTFd/translations

2
babel.cfg Normal file
View File

@@ -0,0 +1,2 @@
[python: CTFd/**/*.py]
[jinja2: CTFd/themes/core-beta/templates/**.html]

View File

@@ -0,0 +1,23 @@
"""Add language column to Users table
Revision ID: 0def790057c1
Revises: 46a278193a94
Create Date: 2023-04-19 00:56:54.592584
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "0def790057c1"
down_revision = "46a278193a94"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("users", sa.Column("language", sa.String(length=32), nullable=True))
def downgrade():
op.drop_column("users", "language")

View File

@@ -30,3 +30,4 @@ maxminddb==1.5.4
tenacity==6.2.0
pybluemonday==0.0.11
freezegun==1.2.2
Flask-Babel==2.0.0

View File

@@ -14,6 +14,8 @@ async-timeout==4.0.2
# via redis
attrs==20.3.0
# via jsonschema
babel==2.12.1
# via flask-babel
bcrypt==4.0.1
# via -r requirements.in
boto3==1.13.9
@@ -41,12 +43,15 @@ docutils==0.15.2
flask==1.1.2
# via
# -r requirements.in
# flask-babel
# flask-caching
# flask-marshmallow
# flask-migrate
# flask-restx
# flask-script
# flask-sqlalchemy
flask-babel==2.0.0
# via -r requirements.in
flask-caching==1.8.0
# via -r requirements.in
flask-marshmallow==0.10.1
@@ -79,6 +84,7 @@ jinja2==2.11.3
# via
# -r requirements.in
# flask
# flask-babel
jmespath==0.10.0
# via
# boto3
@@ -127,8 +133,10 @@ python-editor==1.0.4
python-geoacumen-city==2023.4.15
# via -r requirements.in
pytz==2020.4
# via flask-restx
redis==4.4.4
# via
# flask-babel
# flask-restx
redis==3.5.2
# via -r requirements.in
requests==2.28.1
# via -r requirements.in