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

View File

@@ -1,7 +1,7 @@
import csv # noqa: I001 import csv # noqa: I001
import datetime import datetime
from io import StringIO
import os import os
from io import StringIO
from flask import Blueprint, abort from flask import Blueprint, abort
from flask import current_app as app 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", "hidden",
"banned", "banned",
"verified", "verified",
"language",
"team_id", "team_id",
"created", "created",
], ],

View File

@@ -1,3 +1,4 @@
from flask_babel import lazy_gettext as _l
from wtforms import PasswordField, StringField from wtforms import PasswordField, StringField
from wtforms.fields.html5 import EmailField from wtforms.fields.html5 import EmailField
from wtforms.validators import InputRequired from wtforms.validators import InputRequired
@@ -15,11 +16,11 @@ from CTFd.forms.users import (
def RegistrationForm(*args, **kwargs): def RegistrationForm(*args, **kwargs):
class _RegistrationForm(BaseForm): class _RegistrationForm(BaseForm):
name = StringField( name = StringField(
"User Name", validators=[InputRequired()], render_kw={"autofocus": True} _l("User Name"), validators=[InputRequired()], render_kw={"autofocus": True}
) )
email = EmailField("Email", validators=[InputRequired()]) email = EmailField(_l("Email"), validators=[InputRequired()])
password = PasswordField("Password", validators=[InputRequired()]) password = PasswordField(_l("Password"), validators=[InputRequired()])
submit = SubmitField("Submit") submit = SubmitField(_l("Submit"))
@property @property
def extra(self): def extra(self):
@@ -35,27 +36,27 @@ def RegistrationForm(*args, **kwargs):
class LoginForm(BaseForm): class LoginForm(BaseForm):
name = StringField( name = StringField(
"User Name or Email", _l("User Name or Email"),
validators=[InputRequired()], validators=[InputRequired()],
render_kw={"autofocus": True}, render_kw={"autofocus": True},
) )
password = PasswordField("Password", validators=[InputRequired()]) password = PasswordField(_l("Password"), validators=[InputRequired()])
submit = SubmitField("Submit") submit = SubmitField(_l("Submit"))
class ConfirmForm(BaseForm): class ConfirmForm(BaseForm):
submit = SubmitField("Resend Confirmation Email") submit = SubmitField(_l("Resend Confirmation Email"))
class ResetPasswordRequestForm(BaseForm): class ResetPasswordRequestForm(BaseForm):
email = EmailField( 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): class ResetPasswordForm(BaseForm):
password = PasswordField( 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 import session
from flask_babel import lazy_gettext as _l
from wtforms import PasswordField, SelectField, StringField from wtforms import PasswordField, SelectField, StringField
from wtforms.fields.html5 import DateField, URLField from wtforms.fields.html5 import DateField, URLField
from CTFd.constants.languages import SELECT_LANGUAGE_LIST
from CTFd.forms import BaseForm from CTFd.forms import BaseForm
from CTFd.forms.fields import SubmitField from CTFd.forms.fields import SubmitField
from CTFd.forms.users import attach_custom_user_fields, build_custom_user_fields 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): def SettingsForm(*args, **kwargs):
class _SettingsForm(BaseForm): class _SettingsForm(BaseForm):
name = StringField("User Name") name = StringField(_l("User Name"))
email = StringField("Email") email = StringField(_l("Email"))
password = PasswordField("Password") language = SelectField(_l("Language"), choices=SELECT_LANGUAGE_LIST)
confirm = PasswordField("Current Password") password = PasswordField(_l("Password"))
affiliation = StringField("Affiliation") confirm = PasswordField(_l("Current Password"))
website = URLField("Website") affiliation = StringField(_l("Affiliation"))
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST) website = URLField(_l("Website"))
submit = SubmitField("Submit") country = SelectField(_l("Country"), choices=SELECT_COUNTRIES_LIST)
submit = SubmitField(_l("Submit"))
@property @property
def extra(self): def extra(self):
@@ -46,5 +49,5 @@ def SettingsForm(*args, **kwargs):
class TokensForm(BaseForm): class TokensForm(BaseForm):
expiration = DateField("Expiration") expiration = DateField(_l("Expiration"))
submit = SubmitField("Generate") submit = SubmitField(_l("Generate"))

View File

@@ -1,3 +1,4 @@
from flask_babel import lazy_gettext as _l
from wtforms import ( from wtforms import (
FileField, FileField,
HiddenField, HiddenField,
@@ -18,62 +19,72 @@ from CTFd.utils.config import get_themes
class SetupForm(BaseForm): class SetupForm(BaseForm):
ctf_name = StringField( 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( ctf_description = TextAreaField(
"Event Description", description="Description for the CTF" _l("Event Description"), description=_l("Description for the CTF")
) )
user_mode = RadioField( user_mode = RadioField(
"User Mode", _l("User Mode"),
choices=[("teams", "Team Mode"), ("users", "User Mode")], choices=[("teams", _l("Team Mode")), ("users", _l("User Mode"))],
default="teams", 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()], validators=[InputRequired()],
) )
name = StringField( name = StringField(
"Admin Username", _l("Admin Username"),
description="Your username for the administration account", description=_l("Your username for the administration account"),
validators=[InputRequired()], validators=[InputRequired()],
) )
email = EmailField( email = EmailField(
"Admin Email", _l("Admin Email"),
description="Your email address for the administration account", description=_l("Your email address for the administration account"),
validators=[InputRequired()], validators=[InputRequired()],
) )
password = PasswordField( password = PasswordField(
"Admin Password", _l("Admin Password"),
description="Your password for the administration account", description=_l("Your password for the administration account"),
validators=[InputRequired()], validators=[InputRequired()],
) )
ctf_logo = FileField( ctf_logo = FileField(
"Logo", _l("Logo"),
description="Logo to use for the website instead of a CTF name. Used as the home page button. Optional.", description=_l(
"Logo to use for the website instead of a CTF name. Used as the home page button. Optional."
),
) )
ctf_banner = FileField( 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( ctf_small_icon = FileField(
"Small Icon", _l("Small Icon"),
description="favicon used in user's browsers. Only PNGs accepted. Must be 32x32px. Optional.", description=_l(
"favicon used in user's browsers. Only PNGs accepted. Must be 32x32px. Optional."
),
) )
ctf_theme = SelectField( ctf_theme = SelectField(
"Theme", _l("Theme"),
description="CTFd Theme to use. Can be changed later.", description=_l("CTFd Theme to use. Can be changed later."),
choices=list(zip(get_themes(), get_themes())), choices=list(zip(get_themes(), get_themes())),
default=DEFAULT_THEME, default=DEFAULT_THEME,
validators=[InputRequired()], validators=[InputRequired()],
) )
theme_color = HiddenField( theme_color = HiddenField(
"Theme Color", _l("Theme Color"),
description="Color used by theme to control aesthetics. Requires theme support. Optional.", description=_l(
"Color used by theme to control aesthetics. Requires theme support. Optional."
),
) )
start = StringField( 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 = 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 import BooleanField, PasswordField, SelectField, StringField
from wtforms.fields.html5 import EmailField, URLField from wtforms.fields.html5 import EmailField, URLField
from wtforms.validators import InputRequired from wtforms.validators import InputRequired
@@ -72,16 +73,16 @@ def attach_custom_team_fields(form_cls, **kwargs):
class TeamJoinForm(BaseForm): class TeamJoinForm(BaseForm):
name = StringField("Team Name", validators=[InputRequired()]) name = StringField(_l("Team Name"), validators=[InputRequired()])
password = PasswordField("Team Password", validators=[InputRequired()]) password = PasswordField(_l("Team Password"), validators=[InputRequired()])
submit = SubmitField("Join") submit = SubmitField(_l("Join"))
def TeamRegisterForm(*args, **kwargs): def TeamRegisterForm(*args, **kwargs):
class _TeamRegisterForm(BaseForm): class _TeamRegisterForm(BaseForm):
name = StringField("Team Name", validators=[InputRequired()]) name = StringField(_l("Team Name"), validators=[InputRequired()])
password = PasswordField("Team Password", validators=[InputRequired()]) password = PasswordField(_l("Team Password"), validators=[InputRequired()])
submit = SubmitField("Create") submit = SubmitField(_l("Create"))
@property @property
def extra(self): def extra(self):
@@ -96,30 +97,34 @@ def TeamRegisterForm(*args, **kwargs):
def TeamSettingsForm(*args, **kwargs): def TeamSettingsForm(*args, **kwargs):
class _TeamSettingsForm(BaseForm): class _TeamSettingsForm(BaseForm):
name = StringField( name = StringField(
"Team Name", _l("Team Name"),
description="Your team's public name shown to other competitors", description=_l("Your team's public name shown to other competitors"),
) )
password = PasswordField( 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 = PasswordField(
"Confirm Current Team Password", _l("Confirm Current Team Password"),
description="Provide your current team password (or your password) to update your team's password", description=_l(
"Provide your current team password (or your password) to update your team's password"
),
) )
affiliation = StringField( affiliation = StringField(
"Affiliation", _l("Affiliation"),
description="Your team's affiliation publicly shown to other competitors", description=_l(
"Your team's affiliation publicly shown to other competitors"
),
) )
website = URLField( website = URLField(
"Website", _l("Website"),
description="Your team's website publicly shown to other competitors", description=_l("Your team's website publicly shown to other competitors"),
) )
country = SelectField( country = SelectField(
"Country", _l("Country"),
choices=SELECT_COUNTRIES_LIST, 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 @property
def extra(self): def extra(self):
@@ -156,7 +161,9 @@ def TeamSettingsForm(*args, **kwargs):
class TeamCaptainForm(BaseForm): class TeamCaptainForm(BaseForm):
# Choices are populated dynamically at form creation time # 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") submit = SubmitField("Submit")
@@ -178,29 +185,29 @@ class TeamSearchForm(BaseForm):
class PublicTeamSearchForm(BaseForm): class PublicTeamSearchForm(BaseForm):
field = SelectField( field = SelectField(
"Search Field", _l("Search Field"),
choices=[ choices=[
("name", "Name"), ("name", _l("Name")),
("affiliation", "Affiliation"), ("affiliation", _l("Affiliation")),
("website", "Website"), ("website", _l("Website")),
], ],
default="name", default="name",
validators=[InputRequired()], validators=[InputRequired()],
) )
q = StringField("Parameter", validators=[InputRequired()]) q = StringField(_l("Parameter"), validators=[InputRequired()])
submit = SubmitField("Search") submit = SubmitField(_l("Search"))
class TeamBaseForm(BaseForm): class TeamBaseForm(BaseForm):
name = StringField("Team Name", validators=[InputRequired()]) name = StringField(_l("Team Name"), validators=[InputRequired()])
email = EmailField("Email") email = EmailField(_l("Email"))
password = PasswordField("Password") password = PasswordField(_l("Password"))
website = URLField("Website") website = URLField(_l("Website"))
affiliation = StringField("Affiliation") affiliation = StringField(_l("Affiliation"))
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST) country = SelectField(_l("Country"), choices=SELECT_COUNTRIES_LIST)
hidden = BooleanField("Hidden") hidden = BooleanField(_l("Hidden"))
banned = BooleanField("Banned") banned = BooleanField(_l("Banned"))
submit = SubmitField("Submit") submit = SubmitField(_l("Submit"))
def TeamCreateForm(*args, **kwargs): def TeamCreateForm(*args, **kwargs):
@@ -244,8 +251,8 @@ def TeamEditForm(*args, **kwargs):
class TeamInviteForm(BaseForm): class TeamInviteForm(BaseForm):
link = URLField("Invite Link") link = URLField(_l("Invite Link"))
class TeamInviteJoinForm(BaseForm): 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 import BooleanField, PasswordField, SelectField, StringField
from wtforms.fields.html5 import EmailField from wtforms.fields.html5 import EmailField
from wtforms.validators import InputRequired from wtforms.validators import InputRequired
from CTFd.constants.config import Configs from CTFd.constants.config import Configs
from CTFd.constants.languages import SELECT_LANGUAGE_LIST
from CTFd.forms import BaseForm from CTFd.forms import BaseForm
from CTFd.forms.fields import SubmitField from CTFd.forms.fields import SubmitField
from CTFd.models import UserFieldEntries, UserFields from CTFd.models import UserFieldEntries, UserFields
@@ -130,22 +132,27 @@ class UserSearchForm(BaseForm):
class PublicUserSearchForm(BaseForm): class PublicUserSearchForm(BaseForm):
field = SelectField( field = SelectField(
"Search Field", _l("Search Field"),
choices=[ choices=[
("name", "Name"), ("name", _l("Name")),
("affiliation", "Affiliation"), ("affiliation", _l("Affiliation")),
("website", "Website"), ("website", _l("Website")),
], ],
default="name", default="name",
validators=[InputRequired()], validators=[InputRequired()],
) )
q = StringField("Parameter", validators=[InputRequired()]) q = StringField(
submit = SubmitField("Search") _l("Parameter"),
description=_l("Search for matching users"),
validators=[InputRequired()],
)
submit = SubmitField(_l("Search"))
class UserBaseForm(BaseForm): class UserBaseForm(BaseForm):
name = StringField("User Name", validators=[InputRequired()]) name = StringField("User Name", validators=[InputRequired()])
email = EmailField("Email", validators=[InputRequired()]) email = EmailField("Email", validators=[InputRequired()])
language = SelectField(_l("Language"), choices=SELECT_LANGUAGE_LIST)
password = PasswordField("Password") password = PasswordField("Password")
website = StringField("Website") website = StringField("Website")
affiliation = StringField("Affiliation") affiliation = StringField("Affiliation")

View File

@@ -336,6 +336,7 @@ class Users(db.Model):
hidden = db.Column(db.Boolean, default=False) hidden = db.Column(db.Boolean, default=False)
banned = db.Column(db.Boolean, default=False) banned = db.Column(db.Boolean, default=False)
verified = 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 # Relationship for Teams
team_id = db.Column(db.Integer, db.ForeignKey("teams.id")) 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.crypto import verify_password
from CTFd.utils.email import check_email_is_whitelisted from CTFd.utils.email import check_email_is_whitelisted
from CTFd.utils.user import get_current_user, is_admin 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): class UserSchema(ma.ModelSchema):
@@ -50,6 +50,7 @@ class UserSchema(ma.ModelSchema):
else True else True
], ],
) )
language = field_for(Users, "language", validate=[validate_language])
country = field_for(Users, "country", validate=[validate_country_code]) country = field_for(Users, "country", validate=[validate_country_code])
password = field_for(Users, "password", required=True, allow_none=False) password = field_for(Users, "password", required=True, allow_none=False)
fields = Nested( fields = Nested(
@@ -323,6 +324,7 @@ class UserSchema(ma.ModelSchema):
"website", "website",
"name", "name",
"email", "email",
"language",
"country", "country",
"affiliation", "affiliation",
"bracket", "bracket",
@@ -339,6 +341,7 @@ class UserSchema(ma.ModelSchema):
"country", "country",
"banned", "banned",
"email", "email",
"language",
"affiliation", "affiliation",
"secret", "secret",
"bracket", "bracket",

View File

@@ -9,6 +9,10 @@
{{ form.email.label }} {{ form.email.label }}
{{ form.email(class="form-control", autocomplete="off") }} {{ form.email(class="form-control", autocomplete="off") }}
</div> </div>
<div class="form-group">
{{ form.language.label }}
{{ form.language(class="form-control custom-select", autocomplete="off") }}
</div>
<div class="form-group"> <div class="form-group">
{{ form.password.label }} {{ form.password.label }}
{{ form.password(class="form-control", autocomplete="off") }} {{ 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) COUNTRIES_DICT = OrderedDict(COUNTRIES_LIST)
# List of countries suitable for use in forms # List of countries suitable for use in forms
# TODO: CTFd 4.0 Move SELECT_COUNTRIES_LIST into constants
SELECT_COUNTRIES_LIST = [("", "")] + COUNTRIES_LIST SELECT_COUNTRIES_LIST = [("", "")] + COUNTRIES_LIST

View File

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

View File

@@ -5,7 +5,8 @@ from flask import abort
from flask import current_app as app from flask import current_app as app
from flask import redirect, request, session, url_for 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.teams import TeamAttrs
from CTFd.constants.users import UserAttrs from CTFd.constants.users import UserAttrs
from CTFd.models import Fails, Teams, Tracking, Users, db from CTFd.models import Fails, Teams, Tracking, Users, db
@@ -36,7 +37,11 @@ def get_current_user():
def get_current_user_attrs(): def get_current_user_attrs():
if authed(): 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: else:
return None return None
@@ -94,7 +99,11 @@ def get_current_team():
def get_current_team_attrs(): def get_current_team_attrs():
if authed(): 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: if user and user.team_id:
return get_team_attrs(team_id=user.team_id) return get_team_attrs(team_id=user.team_id)
return None return None
@@ -167,6 +176,15 @@ def get_ip(req=None):
return remote_addr 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(): def get_current_user_recent_ips():
if authed(): if authed():
return get_user_recent_ips(user_id=session["id"]) 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 flask import request
from marshmallow import ValidationError from marshmallow import ValidationError
from CTFd.constants.languages import LANGUAGE_NAMES
from CTFd.models import Users from CTFd.models import Users
from CTFd.utils.countries import lookup_country_code from CTFd.utils.countries import lookup_country_code
from CTFd.utils.user import get_current_user, is_admin from CTFd.utils.user import get_current_user, is_admin
@@ -39,3 +40,10 @@ def validate_country_code(country_code):
return return
if lookup_country_code(country_code) is None: if lookup_country_code(country_code) is None:
raise ValidationError("Invalid Country") 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() errors = get_errors()
user = get_current_user() 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: if is_teams_mode() and get_current_team() is None:
team_url = url_for("teams.private") team_url = url_for("teams.private")
@@ -346,11 +341,12 @@ def settings():
return render_template( return render_template(
"settings.html", "settings.html",
name=name, name=user.name,
email=email, email=user.email,
website=website, language=user.language,
affiliation=affiliation, website=user.website,
country=country, affiliation=user.affiliation,
country=user.country,
tokens=tokens, tokens=tokens,
prevent_name_change=prevent_name_change, prevent_name_change=prevent_name_change,
infos=infos, infos=infos,

View File

@@ -30,3 +30,15 @@ serve:
shell: shell:
python manage.py 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 tenacity==6.2.0
pybluemonday==0.0.11 pybluemonday==0.0.11
freezegun==1.2.2 freezegun==1.2.2
Flask-Babel==2.0.0

View File

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