Merge branch 'v4.x' of gitlab.com:allianceauth/allianceauth into v4docs

This commit is contained in:
Ariel Rin 2024-02-23 22:26:38 +10:00
commit 445683c3d5
No known key found for this signature in database
173 changed files with 5993 additions and 2913 deletions

View File

@ -19,5 +19,6 @@ exclude_lines =
if __name__ == .__main__.: if __name__ == .__main__.:
def __repr__ def __repr__
raise AssertionError raise AssertionError
if TYPE_CHECKING:
ignore_errors = True ignore_errors = True

View File

@ -26,11 +26,11 @@ pre-commit-check:
<<: *only-default <<: *only-default
stage: pre-commit stage: pre-commit
image: python:3.11-bullseye image: python:3.11-bullseye
variables: # variables:
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit # PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
cache: # cache:
paths: # paths:
- ${PRE_COMMIT_HOME} # - ${PRE_COMMIT_HOME}
script: script:
- pip install pre-commit - pip install pre-commit
- pre-commit run --all-files - pre-commit run --all-files

View File

@ -12,6 +12,9 @@ build:
- redis - redis
tools: tools:
python: "3.11" python: "3.11"
jobs:
post_system_dependencies:
- redis-server --daemonize yes
# Build documentation in the docs/ directory with Sphinx # Build documentation in the docs/ directory with Sphinx
sphinx: sphinx:

View File

@ -5,7 +5,7 @@ manage online service access.
# This will make sure the app is always imported when # This will make sure the app is always imported when
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.
__version__ = '4.0.0a4' __version__ = '4.0.0b1'
__title__ = 'Alliance Auth' __title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth' __url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = f'{__title__} v{__version__}' NAME = f'{__title__} v{__version__}'

View File

@ -1,6 +1,6 @@
from allianceauth.hooks import DashboardItemHook from allianceauth.hooks import DashboardItemHook
from allianceauth import hooks from allianceauth import hooks
from .views import dashboard_characters, dashboard_groups, dashboard_admin from .views import dashboard_characters, dashboard_esi_check, dashboard_groups, dashboard_admin
class UserCharactersHook(DashboardItemHook): class UserCharactersHook(DashboardItemHook):
@ -26,6 +26,15 @@ class AdminHook(DashboardItemHook):
DashboardItemHook.__init__( DashboardItemHook.__init__(
self, self,
dashboard_admin, dashboard_admin,
1
)
class ESICheckHook(DashboardItemHook):
def __init__(self):
DashboardItemHook.__init__(
self,
dashboard_esi_check,
0 0
) )
@ -43,3 +52,8 @@ def register_groups_hook():
@hooks.register('dashboard_hook') @hooks.register('dashboard_hook')
def register_admin_hook(): def register_admin_hook():
return AdminHook() return AdminHook()
@hooks.register('dashboard_hook')
def register_esi_hook():
return ESICheckHook()

View File

@ -0,0 +1,12 @@
from django.utils.translation import gettext_lazy as _
# Overide ESI messages in the dashboard widget
# when the returned messages are not helpful or out of date
ESI_ERROR_MESSAGE_OVERRIDES = {
420: _("This software has exceeded the error limit for ESI. "
"If you are a user, please contact the maintainer of this software."
" If you are a developer/maintainer, please make a greater "
"effort in the future to receive valid responses. For tips on how, "
"come have a chat with us in ##3rd-party-dev-and-esi on the EVE "
"Online Discord. https://www.eveonline.com/discord")
}

View File

@ -0,0 +1,10 @@
{% extends 'allianceauth/base.html' %}
{% block page_title %}Dashboard{% endblock page_title %}
{% block content %}
<div>
<h1>Dashboard Dummy</h1>
</div>
{% endblock %}

View File

@ -1,5 +1,5 @@
{% load i18n %} {% load i18n %}
<div class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2"> <div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">

View File

@ -1,5 +1,5 @@
{% load i18n %} {% load i18n %}
<div class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2"> <div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2">
<div class="card h-100"> <div class="card h-100">
<div class="card-body"> <div class="card-body">
<h4 class="card-title text-center">{% translate "Membership" %}</h4> <h4 class="card-title text-center">{% translate "Membership" %}</h4>

View File

@ -16,7 +16,7 @@
{% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://community.eveonline.com/support/third-party-applications/ where possible."|urlize %} {% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://community.eveonline.com/support/third-party-applications/ where possible."|urlize %}
</p> </p>
<table class="table" id="table_tokens" style="width: 100%;"> <table class="table w-100" id="table_tokens">
<thead> <thead>
<tr> <tr>
<th>{% translate "Scopes" %}</th> <th>{% translate "Scopes" %}</th>

View File

@ -4,7 +4,7 @@
<form action="{% url 'set_language' %}" method="post"> <form action="{% url 'set_language' %}" method="post">
{% csrf_token %} {% csrf_token %}
<select onchange="this.form.submit()" class="form-control" id="lang-select" name="language"> <select class="form-select" onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
{% get_language_info_list for LANGUAGES as languages %} {% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %} {% for language in languages %}

View File

@ -1,6 +1,6 @@
{% extends 'public/base.html' %} {% extends 'public/base.html' %}
{% load bootstrap %} {% load django_bootstrap5 %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% translate "Registration" %}{% endblock %} {% block page_title %}{% translate "Registration" %}{% endblock %}
@ -12,16 +12,20 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-md-4 col-md-offset-4"> <div class="row justify-content-center">
<div class="panel panel-default panel-transparent"> <div class="col-md-4">
<div class="panel-body"> <div class="card card-login border-secondary p-3">
<div class="card-body">
<form method="POST"> <form method="POST">
{% csrf_token %} {% csrf_token %}
{{ form|bootstrap }} {% bootstrap_form form %}
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Register" %}</button>
<button class="btn btn-primary btn-block" type="submit">{% translate "Register" %}</button>
</form> </form>
</div>
</div>
{% include 'public/lang_select.html' %} {% include 'public/lang_select.html' %}
</div> </div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,12 @@
import json import json
import requests_mock
from unittest.mock import patch from unittest.mock import patch
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from allianceauth.authentication.views import task_counts from allianceauth.authentication.views import task_counts, esi_check
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.authentication.constants import ESI_ERROR_MESSAGE_OVERRIDES
MODULE_PATH = "allianceauth.authentication.views" MODULE_PATH = "allianceauth.authentication.views"
@ -21,6 +23,8 @@ class TestRunningTasksCount(TestCase):
super().setUpClass() super().setUpClass()
cls.factory = RequestFactory() cls.factory = RequestFactory()
cls.user = AuthUtils.create_user("bruce_wayne") cls.user = AuthUtils.create_user("bruce_wayne")
cls.user.is_superuser = True
cls.user.save()
def test_should_return_data( def test_should_return_data(
self, mock_active_tasks_count, mock_queued_tasks_count self, mock_active_tasks_count, mock_queued_tasks_count
@ -35,5 +39,164 @@ class TestRunningTasksCount(TestCase):
# then # then
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertDictEqual( self.assertDictEqual(
jsonresponse_to_dict(response), {"tasks_running": 2, "tasks_queued": 3} jsonresponse_to_dict(response), {
"tasks_running": 2, "tasks_queued": 3}
) )
def test_su_only(
self, mock_active_tasks_count, mock_queued_tasks_count
):
self.user.is_superuser = False
self.user.save()
self.user.refresh_from_db()
# given
mock_active_tasks_count.return_value = 2
mock_queued_tasks_count.return_value = 3
request = self.factory.get("/")
request.user = self.user
# when
response = task_counts(request)
# then
self.assertEqual(response.status_code, 302)
class TestEsiCheck(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.factory = RequestFactory()
cls.user = AuthUtils.create_user("bruce_wayne")
cls.user.is_superuser = True
cls.user.save()
@requests_mock.Mocker()
def test_401_data_returns_200(
self, m
):
error_json = {
"error": "You have been banned from using ESI. Please contact Technical Support. (support@eveonline.com)"
}
status_code = 401
m.get(
"https://esi.evetech.net/latest/status/?datasource=tranquility",
text=json.dumps(error_json),
status_code=status_code
)
# given
request = self.factory.get("/")
request.user = self.user
# when
response = esi_check(request)
# then
self.assertEqual(response.status_code, 200)
self.assertDictEqual(
jsonresponse_to_dict(response), {
"status": status_code,
"data": error_json
}
)
@requests_mock.Mocker()
def test_504_data_returns_200(
self, m
):
error_json = {
"error": "Gateway timeout message",
"timeout": 5000
}
status_code = 504
m.get(
"https://esi.evetech.net/latest/status/?datasource=tranquility",
text=json.dumps(error_json),
status_code=status_code
)
# given
request = self.factory.get("/")
request.user = self.user
# when
response = esi_check(request)
# then
self.assertEqual(response.status_code, 200)
self.assertDictEqual(
jsonresponse_to_dict(response), {
"status": status_code,
"data": error_json
}
)
@requests_mock.Mocker()
def test_420_data_override(
self, m
):
error_json = {
"error": "message from CCP",
}
status_code = 420
m.get(
"https://esi.evetech.net/latest/status/?datasource=tranquility",
text=json.dumps(error_json),
status_code=status_code
)
# given
request = self.factory.get("/")
request.user = self.user
# when
response = esi_check(request)
# then
self.assertEqual(response.status_code, 200)
self.assertNotEqual(
jsonresponse_to_dict(response)["data"],
error_json
)
self.assertDictEqual(
jsonresponse_to_dict(response), {
"status": status_code,
"data": {
"error": ESI_ERROR_MESSAGE_OVERRIDES.get(status_code)
}
}
)
@requests_mock.Mocker()
def test_200_data_returns_200(
self, m
):
good_json = {
"players": 5,
"server_version": "69420",
"start_time": "2030-01-01T23:59:59Z"
}
status_code = 200
m.get(
"https://esi.evetech.net/latest/status/?datasource=tranquility",
text=json.dumps(good_json),
status_code=status_code
)
# given
request = self.factory.get("/")
request.user = self.user
# when
response = esi_check(request)
# then
self.assertEqual(response.status_code, 200)
self.assertDictEqual(
jsonresponse_to_dict(response), {
"status": status_code,
"data": good_json
}
)
def test_su_only(
self,
):
self.user.is_superuser = False
self.user.save()
self.user.refresh_from_db()
# given
request = self.factory.get("/")
request.user = self.user
# when
response = esi_check(request)
# then
self.assertEqual(response.status_code, 302)

View File

@ -38,5 +38,7 @@ urlpatterns = [
name='token_refresh' name='token_refresh'
), ),
path('dashboard/', views.dashboard, name='dashboard'), path('dashboard/', views.dashboard, name='dashboard'),
path('dashboard_bs3/', views.dashboard_bs3, name='dashboard_bs3'),
path('task-counts/', views.task_counts, name='task_counts'), path('task-counts/', views.task_counts, name='task_counts'),
path('esi-check/', views.esi_check, name='esi_check'),
] ]

View File

@ -1,6 +1,6 @@
import logging import logging
from allianceauth.hooks import get_hooks
import requests
from django_registration.backends.activation.views import ( from django_registration.backends.activation.views import (
REGISTRATION_SALT, ActivationView as BaseActivationView, REGISTRATION_SALT, ActivationView as BaseActivationView,
RegistrationView as BaseRegistrationView, RegistrationView as BaseRegistrationView,
@ -10,7 +10,7 @@ from django_registration.signals import user_registered
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core import signing from django.core import signing
from django.http import JsonResponse from django.http import JsonResponse
@ -23,14 +23,16 @@ from esi.decorators import token_required
from esi.models import Token from esi.models import Token
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
from allianceauth.hooks import get_hooks
from .constants import ESI_ERROR_MESSAGE_OVERRIDES
from .core.celery_workers import active_tasks_count, queued_tasks_count from .core.celery_workers import active_tasks_count, queued_tasks_count
from .forms import RegistrationForm from .forms import RegistrationForm
from .models import CharacterOwnership from .models import CharacterOwnership
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS: if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True _has_auto_groups = True
from allianceauth.eveonline.autogroups.models import * from allianceauth.eveonline.autogroups.models import * # noqa: F401, F403
else: else:
_has_auto_groups = False _has_auto_groups = False
@ -54,7 +56,7 @@ def dashboard_groups(request):
context = { context = {
'groups': groups, 'groups': groups,
} }
return render_to_string('authentication/dashboard.groups.html', context=context, request=request) return render_to_string('authentication/dashboard_groups.html', context=context, request=request)
def dashboard_characters(request): def dashboard_characters(request):
@ -66,7 +68,7 @@ def dashboard_characters(request):
context = { context = {
'characters': characters 'characters': characters
} }
return render_to_string('authentication/dashboard.characters.html', context=context, request=request) return render_to_string('authentication/dashboard_characters.html', context=context, request=request)
def dashboard_admin(request): def dashboard_admin(request):
@ -76,6 +78,13 @@ def dashboard_admin(request):
return "" return ""
def dashboard_esi_check(request):
if request.user.is_superuser:
return render_to_string('allianceauth/admin-status/esi_check.html', request=request)
else:
return ""
@login_required @login_required
def dashboard(request): def dashboard(request):
_dash_items = list() _dash_items = list()
@ -135,23 +144,30 @@ def token_refresh(request, token_id=None):
@login_required @login_required
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES) @token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
def main_character_change(request, token): def main_character_change(request, token):
logger.debug(f"main_character_change called by user {request.user} for character {token.character_name}") logger.debug(
f"main_character_change called by user {request.user} for character {token.character_name}")
try: try:
co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user) co = CharacterOwnership.objects.get(
character__character_id=token.character_id, user=request.user)
except CharacterOwnership.DoesNotExist: except CharacterOwnership.DoesNotExist:
if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists(): if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists():
co = CharacterOwnership.objects.create_by_token(token) co = CharacterOwnership.objects.create_by_token(token)
else: else:
messages.error( messages.error(
request, request,
_('Cannot change main character to %(char)s: character owned by a different account.') % ({'char': token.character_name}) _('Cannot change main character to %(char)s: character owned by a different account.') % (
{'char': token.character_name})
) )
co = None co = None
if co: if co:
request.user.profile.main_character = co.character request.user.profile.main_character = co.character
request.user.profile.save(update_fields=['main_character']) request.user.profile.save(update_fields=['main_character'])
messages.success(request, _('Changed main character to %(char)s') % {"char": co.character}) messages.success(request, _('Changed main character to %s') % co.character)
logger.info('Changed user %(user)s main character to %(char)s' % ({'user': request.user, 'char': co.character})) logger.info(
'Changed user {user} main character to {char}'.format(
user=request.user, char=co.character
)
)
return redirect("authentication:dashboard") return redirect("authentication:dashboard")
@ -159,9 +175,11 @@ def main_character_change(request, token):
def add_character(request, token): def add_character(request, token):
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter( if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
owner_hash=token.character_owner_hash).filter(user=request.user).exists(): owner_hash=token.character_owner_hash).filter(user=request.user).exists():
messages.success(request, _('Added %(name)s to your account.' % ({'name': token.character_name}))) messages.success(request, _(
'Added %(name)s to your account.' % ({'name': token.character_name})))
else: else:
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % ({'name': token.character_name}))) messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % (
{'name': token.character_name})))
return redirect('authentication:dashboard') return redirect('authentication:dashboard')
@ -204,8 +222,10 @@ def sso_login(request, token):
token.delete() token.delete()
messages.error( messages.error(
request, request,
_('Unable to authenticate as the selected character. ' _(
'Please log in with the main character associated with this account.') 'Unable to authenticate as the selected character. '
'Please log in with the main character associated with this account.'
)
) )
return redirect(settings.LOGIN_URL) return redirect(settings.LOGIN_URL)
@ -278,7 +298,8 @@ class RegistrationView(BaseRegistrationView):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def register(self, form): def register(self, form):
user = User.objects.get(pk=self.request.session.get('registration_uid')) user = User.objects.get(
pk=self.request.session.get('registration_uid'))
user.email = form.cleaned_data['email'] user.email = form.cleaned_data['email']
user_registered.send(self.__class__, user=user, request=self.request) user_registered.send(self.__class__, user=user, request=self.request)
if getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True): if getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
@ -295,7 +316,8 @@ class RegistrationView(BaseRegistrationView):
def get_email_context(self, activation_key): def get_email_context(self, activation_key):
context = super().get_email_context(activation_key) context = super().get_email_context(activation_key)
context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key]) context['url'] = context['site'].domain + \
reverse('registration_activate', args=[activation_key])
return context return context
@ -328,20 +350,24 @@ class ActivationView(BaseActivationView):
def registration_complete(request): def registration_complete(request):
messages.success(request, _('Sent confirmation email. Please follow the link to confirm your email address.')) messages.success(request, _(
'Sent confirmation email. Please follow the link to confirm your email address.'))
return redirect('authentication:login') return redirect('authentication:login')
def activation_complete(request): def activation_complete(request):
messages.success(request, _('Confirmed your email address. Please login to continue.')) messages.success(request, _(
'Confirmed your email address. Please login to continue.'))
return redirect('authentication:dashboard') return redirect('authentication:dashboard')
def registration_closed(request): def registration_closed(request):
messages.error(request, _('Registration of new accounts is not allowed at this time.')) messages.error(request, _(
'Registration of new accounts is not allowed at this time.'))
return redirect('authentication:login') return redirect('authentication:login')
@user_passes_test(lambda u: u.is_superuser)
def task_counts(request) -> JsonResponse: def task_counts(request) -> JsonResponse:
"""Return task counts as JSON for an AJAX call.""" """Return task counts as JSON for an AJAX call."""
data = { data = {
@ -349,3 +375,31 @@ def task_counts(request) -> JsonResponse:
"tasks_queued": queued_tasks_count() "tasks_queued": queued_tasks_count()
} }
return JsonResponse(data) return JsonResponse(data)
def check_for_override_esi_error_message(response):
if response.status_code in ESI_ERROR_MESSAGE_OVERRIDES:
return {"error": ESI_ERROR_MESSAGE_OVERRIDES.get(response.status_code)}
else:
return response.json()
@user_passes_test(lambda u: u.is_superuser)
def esi_check(request) -> JsonResponse:
"""Return if ESI ok With error messages and codes as JSON"""
_r = requests.get("https://esi.evetech.net/latest/status/?datasource=tranquility")
data = {
"status": _r.status_code,
"data": check_for_override_esi_error_message(_r)
}
return JsonResponse(data)
@login_required
def dashboard_bs3(request):
"""Render dashboard view with BS3 theme.
This is an internal view used for testing BS3 backward compatibility in AA4 only.
"""
return render(request, 'authentication/dashboard_bs3.html')

View File

@ -31,7 +31,7 @@
<div class="card card-default mt-4"> <div class="card card-default mt-4">
<div class="card-header clearfix" role="tablist"> <div class="card-header clearfix" role="tablist">
<ul class="nav nav-pills text-right float-start"> <ul class="nav nav-pills float-start">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<a <a
class="nav-link active" class="nav-link active"

View File

@ -0,0 +1,57 @@
"""
Alliance Auth Evecharacter API
"""
from typing import Optional
from django.contrib.auth.models import User
from allianceauth.authentication.models import CharacterOwnership
from allianceauth.eveonline.models import EveCharacter
from allianceauth.framework.api.user import get_sentinel_user
def get_main_character_from_evecharacter(
character: EveCharacter,
) -> Optional[EveCharacter]:
"""
Get the main character for a given EveCharacter or None when no main character is set
:param character:
:type character:
:return:
:rtype:
"""
try:
userprofile = character.character_ownership.user.profile
except (
AttributeError,
EveCharacter.character_ownership.RelatedObjectDoesNotExist,
CharacterOwnership.user.RelatedObjectDoesNotExist,
):
return None
return userprofile.main_character
def get_user_from_evecharacter(character: EveCharacter) -> User:
"""
Get the user for an EveCharacter or the sentinel user when no user is found
:param character:
:type character:
:return:
:rtype:
"""
try:
userprofile = character.character_ownership.user.profile
except (
AttributeError,
EveCharacter.character_ownership.RelatedObjectDoesNotExist,
CharacterOwnership.user.RelatedObjectDoesNotExist,
):
return get_sentinel_user()
return userprofile.user

View File

@ -6,17 +6,33 @@ from typing import Optional
from django.contrib.auth.models import User from django.contrib.auth.models import User
from allianceauth.authentication.models import CharacterOwnership
from allianceauth.eveonline.models import EveCharacter from allianceauth.eveonline.models import EveCharacter
def get_sentinel_user() -> User: def get_all_characters_from_user(user: User) -> list:
""" """
Get the sentinel user or create one Get all characters from a user or an empty list
when no characters are found for the user or the user is None
:param user:
:type user:
:return: :return:
:rtype:
""" """
return User.objects.get_or_create(username="deleted")[0] if user is None:
return []
try:
characters = [
char.character for char in CharacterOwnership.objects.filter(user=user)
]
except AttributeError:
return []
return characters
def get_main_character_from_user(user: User) -> Optional[EveCharacter]: def get_main_character_from_user(user: User) -> Optional[EveCharacter]:
""" """
@ -62,3 +78,13 @@ def get_main_character_name_from_user(user: User) -> str:
return str(user) return str(user)
return username return username
def get_sentinel_user() -> User:
"""
Get the sentinel user or create one
:return:
"""
return User.objects.get_or_create(username="deleted")[0]

View File

@ -13,6 +13,45 @@
} }
} }
/* Side Navigation
------------------------------------------------------------------------------------- */
@media all {
#sidebar > div {
width: 325px;
}
/* Menu items in general */
#sidebar-menu li > a,
#sidebar-menu li > ul > li > a {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
max-width: 210px;
}
/* Parent items with chevron and possible badge */
#sidebar-menu li:has(span.badge) > a[data-bs-toggle="collapse"] {
max-width: 180px;
}
/* Child items with possible badge */
#sidebar-menu li > ul > li > a {
max-width: 189px;
}
/* Chevron icons */
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-down,
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-right {
display: block;
width: 16px;
}
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-right,
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-down {
display: none;
}
}
/* Cursor classes /* Cursor classes
------------------------------------------------------------------------------------- */ ------------------------------------------------------------------------------------- */
@media all { @media all {
@ -72,9 +111,16 @@
border: 1px solid var(--bs-border-color); border: 1px solid var(--bs-border-color);
border-left-width: 0.25rem; border-left-width: 0.25rem;
border-radius: 0.25rem; border-radius: 0.25rem;
margin-bottom: 1.25rem; margin-bottom: 1rem;
margin-top: 1.25rem; padding: 1rem;
padding: 1.25rem; }
.aa-callout.aa-callout-sm {
padding: 0.5rem;
}
.aa-callout.aa-callout-lg {
padding: 1.5rem;
} }
/* Last item bottom margin should be 0 */ /* Last item bottom margin should be 0 */

View File

@ -71,7 +71,7 @@
{% block extra_javascript %} {% block extra_javascript %}
{% include 'bundles/datatables-js-bs5.html' %} {% include 'bundles/datatables-js-bs5.html' %}
{% include 'bundles/moment-js.html' with locale=True %} {% include 'bundles/moment-js.html' with locale=True %}
{# {% include 'bundles/filterdropdown-js.html' %}#} {% include 'bundles/filterdropdown-js.html' %}
<script> <script>
$.fn.dataTable.moment = (format, locale) => { $.fn.dataTable.moment = (format, locale) => {
@ -117,7 +117,8 @@
idx: 6 idx: 6
} }
], ],
bootstrap: true bootstrap: true,
bootstrap_version: 5
}, },
"stateSave": true, "stateSave": true,
"stateDuration": 0 "stateDuration": 0

View File

@ -30,7 +30,11 @@
<tr> <tr>
<th>{% translate "Name" %}</th> <th>{% translate "Name" %}</th>
<th>{% translate "Description" %}</th> <th>{% translate "Description" %}</th>
<th>{% translate "Leaders" %}<span class="m-1 fw-lighter badge bg-primary">{% translate "User" %}</span><span class="m-1 fw-lighter badge bg-secondary ">{% translate "Group" %}</span></th> <th>
{% translate "Leaders" %}<br>
<span class="my-1 me-1 fw-lighter badge bg-primary">{% translate "User" %}</span>
<span class="my-1 me-1 fw-lighter badge bg-secondary">{% translate "Group" %}</span>
</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -39,13 +43,23 @@
{% for g in groups %} {% for g in groups %}
<tr> <tr>
<td>{{ g.group.name }}</td> <td>{{ g.group.name }}</td>
<td>{{ g.group.authgroup.description|linebreaks|urlize }}</td> <td>
{% if g.group.authgroup.description %}
{{ g.group.authgroup.description|linebreaks|urlize }}
{% endif %}
</td>
<td style="max-width: 30%;"> <td style="max-width: 30%;">
{% if g.group.authgroup.group_leaders.all.count %} {% if g.group.authgroup.group_leaders.all.count %}
{% for leader in g.group.authgroup.group_leaders.all %}{% if leader.profile.main_character %}<span class="m-1 badge bg-primary">{{leader.profile.main_character}}</span>{% endif %}{% endfor %} {% for leader in g.group.authgroup.group_leaders.all %}
{% if leader.profile.main_character %}
<span class="my-1 me-1 badge bg-primary">{{leader.profile.main_character}}</span>
{% endif %}
{% endfor %}
{% endif %} {% endif %}
{% if g.group.authgroup.group_leaders.all.count %} {% if g.group.authgroup.group_leaders.all.count %}
{% for group in g.group.authgroup.group_leader_groups.all %}<span class="badge bg-secondary">{{group.name}}</span>{% endfor %} {% for group in g.group.authgroup.group_leader_groups.all %}
<span class="my-1 me-1 badge bg-secondary">{{group.name}}</span>
{% endfor %}
{% endif %} {% endif %}
</td> </td>
<td class="text-end"> <td class="text-end">

View File

@ -13,8 +13,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Peter Pfeufer, 2023\n" "Last-Translator: Peter Pfeufer, 2023\n"
"Language-Team: German (https://app.transifex.com/alliance-auth/teams/107430/de/)\n" "Language-Team: German (https://app.transifex.com/alliance-auth/teams/107430/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -31,7 +31,7 @@ msgstr "Google Analytics Universal"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google Analytics V4" msgstr "Google Analytics V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
"Zur Ausführung dieser Aktion ist ein Hauptcharakter erforderlich. Füge unten" "Zur Ausführung dieser Aktion ist ein Hauptcharakter erforderlich. Füge unten"
@ -47,63 +47,68 @@ msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "" msgstr ""
"Du kannst diese eingeschränkten Gruppen nicht hinzufügen oder entfernen: %s" "Du kannst diese eingeschränkten Gruppen nicht hinzufügen oder entfernen: %s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "Englisch" msgstr "Englisch"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "Spanisch" msgstr "Spanisch"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinesisch vereinfacht" msgstr "Chinesisch vereinfacht"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "Russisch" msgstr "Russisch"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "Koreanisch" msgstr "Koreanisch"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "Japanisch" msgstr "Japanisch"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "Italienisch" msgstr "Italienisch"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr "Ukrainisch"
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "Sprache" msgstr "Sprache"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "Nachtmodus" msgstr "Nachtmodus"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "Status geändert zu %s" msgstr "Status geändert zu %s"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "Dein Nutzerstatus ist nun %(state)s" msgstr "Dein Nutzerstatus ist nun %(state)s"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "Dashboard" msgstr "Dashboard"
@ -161,8 +166,54 @@ msgstr "Corp"
msgid "Alliance" msgid "Alliance"
msgstr "Allianz" msgstr "Allianz"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr "Token-Verwaltung"
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr "Geltungsbereiche"
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Aktionen"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Charakter"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
"Diese Seite ist der beste Versuch, aber Sicherungen oder Datenbankprotokolle"
" können weiterhin Deine Token enthalten. Widerrufe die Token nach "
"Möglichkeit immer auf https://community.eveonline.com/support/third-party-"
"applications/."
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "Einloggen" msgstr "Einloggen"
@ -195,7 +246,7 @@ msgstr "Registrieren"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "Ungültiger oder abgelaufener Aktivierungslink." msgstr "Ungültiger oder abgelaufener Aktivierungslink."
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
@ -204,32 +255,34 @@ msgstr ""
"Der Haputcharakter kann nicht zu %(char)s geändert werden. Dieser Charakter " "Der Haputcharakter kann nicht zu %(char)s geändert werden. Dieser Charakter "
"gehört zu einem anderen Konto." "gehört zu einem anderen Konto."
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "Haupcharakter zu %(char)s geändert" msgstr "Haupcharakter zu %(char)s geändert"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "%(name)s zu Deinem Konto hinzugefügt." msgstr "%(name)s zu Deinem Konto hinzugefügt."
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "" msgstr ""
"Es ist nicht möglich %(name)s zu Deinem Konto hinzu zu fügen: Dieser hat " "Es ist nicht möglich %(name)s zu Deinem Konto hinzu zu fügen: Dieser hat "
"bereits ein eigenes Konto." "bereits ein eigenes Konto."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "Authentifizierung mit dem ausgewählten Charakter nicht möglich." "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "Token zur Registrierung ist abgelaufen." msgstr "Token zur Registrierung ist abgelaufen."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
@ -237,11 +290,11 @@ msgstr ""
"Bestätigungs-E-Mail gesendet. Bitte folge dem Link, um Deine E-Mail-Adresse " "Bestätigungs-E-Mail gesendet. Bitte folge dem Link, um Deine E-Mail-Adresse "
"zu bestätigen." "zu bestätigen."
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "Deine E-Mail Adresse wurde bestätigt. Bitte einloggen zum Fortfahren." msgstr "Deine E-Mail Adresse wurde bestätigt. Bitte einloggen zum Fortfahren."
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "Registrierung von neuen Konten ist zur Zeit nicht erlaubt." msgstr "Registrierung von neuen Konten ist zur Zeit nicht erlaubt."
@ -284,19 +337,6 @@ msgstr "Nicht registriert"
msgid "Last update:" msgid "Last update:"
msgstr "Letzte Aktualisierung:" msgstr "Letzte Aktualisierung:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Charakter"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -630,20 +670,25 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "Gruppenverwaltung" msgstr "Gruppenverwaltung"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Nutzer"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "" msgstr ""
"Dieser Name ist reserviert und kann nicht als Gruppenname genutzt werden." "Dieser Name ist reserviert und kann nicht als Gruppenname genutzt werden."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(automatisch)" msgstr "(automatisch)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "Es existiert bereits eine Gruppe mit diesem Namen." msgstr "Es existiert bereits eine Gruppe mit diesem Namen."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -653,13 +698,13 @@ msgstr ""
"<br>Dies ist für Gruppen genutzt wie Mitglieder, Corp_*, Allianz_*, " "<br>Dies ist für Gruppen genutzt wie Mitglieder, Corp_*, Allianz_*, "
"etc.<br><b>Überschreibt die Versteckt und Offen Option wenn gesetzt</b>" "etc.<br><b>Überschreibt die Versteckt und Offen Option wenn gesetzt</b>"
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
"Diese Gruppe ist nicht sichtbar, aber Nutzer können dennoch beitreten wenn " "Diese Gruppe ist nicht sichtbar, aber Nutzer können dennoch beitreten wenn "
"diese den Link hierzu haben." "diese den Link hierzu haben."
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -668,7 +713,7 @@ msgstr ""
"Anfrage.<br>Wenn die Gruppe nicht offen ist, müssen Anfragen manuell " "Anfrage.<br>Wenn die Gruppe nicht offen ist, müssen Anfragen manuell "
"bestätigt werden." "bestätigt werden."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -680,7 +725,7 @@ msgstr ""
" wird Nutzer nicht von dieser Gruppe entfernen wenn diese nicht länger " " wird Nutzer nicht von dieser Gruppe entfernen wenn diese nicht länger "
"authentifiziert sind." "authentifiziert sind."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
@ -688,7 +733,7 @@ msgstr ""
"Gruppe ist eingeschränkt. Das bedeutet, dass zum Hinzufügen oder Entfernen " "Gruppe ist eingeschränkt. Das bedeutet, dass zum Hinzufügen oder Entfernen "
"von Benutzern für diese Gruppe ein Superuser/Administrator erforderlich ist." "von Benutzern für diese Gruppe ein Superuser/Administrator erforderlich ist."
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -698,7 +743,7 @@ msgstr ""
"<code>auth.group_management</code> Berechtigung um Nutzern zu erlauben alle " "<code>auth.group_management</code> Berechtigung um Nutzern zu erlauben alle "
"Gruppen zu verwalten<br>" "Gruppen zu verwalten<br>"
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -708,7 +753,7 @@ msgstr ""
"Nutze die <code>auth.group_management</code> Berechtigung um Nutzern zu " "Nutze die <code>auth.group_management</code> Berechtigung um Nutzern zu "
"erlauben alle Gruppen zu verwalten.<br>" "erlauben alle Gruppen zu verwalten.<br>"
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
@ -716,42 +761,42 @@ msgstr ""
"Die hier aufgeführten Status können dieser Gruppe beitreten, sofern sie über" "Die hier aufgeführten Status können dieser Gruppe beitreten, sofern sie über"
" die entsprechenden Berechtigungen verfügen.<br>" " die entsprechenden Berechtigungen verfügen.<br>"
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
"Kurze Beschreibung <i>(max. 512 Zeichen)</i> der Gruppe die dem Nutzer " "Kurze Beschreibung <i>(max. 512 Zeichen)</i> der Gruppe die dem Nutzer "
"angezeigt wird." "angezeigt wird."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "Kann nicht öffentlichen Gruppen beitreten" msgstr "Kann nicht öffentlichen Gruppen beitreten"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "Name" msgstr "Name"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "Name kann nicht für Gruppen genutzt werden" msgstr "Name kann nicht für Gruppen genutzt werden"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "Grund" msgstr "Grund"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "Grund wieso dieser Name reserviert ist." msgstr "Grund wieso dieser Name reserviert ist."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "Erstellt bei" msgstr "Erstellt bei"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "Erstellt" msgstr "Erstellt"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "Datum der Erstellung dieses Eintrags" msgstr "Datum der Erstellung dieses Eintrags"
@ -978,26 +1023,26 @@ msgstr "Gruppenanfragen"
msgid "Group Membership" msgid "Group Membership"
msgstr "Gruppenmitgliedschaft" msgstr "Gruppenmitgliedschaft"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "Benutzer %(user)s von Gruppe %(group)s entfernt." msgstr "Benutzer %(user)s von Gruppe %(group)s entfernt."
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "Benutzer existiert nicht in dieser Gruppe" msgstr "Benutzer existiert nicht in dieser Gruppe"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "Gruppe existiert nicht" msgstr "Gruppe existiert nicht"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "Beitrittsanfrage von %(mainchar)s zur Gruppe %(group)s akzeptiert." msgstr "Beitrittsanfrage von %(mainchar)s zur Gruppe %(group)s akzeptiert."
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1006,18 +1051,18 @@ msgstr ""
"Bei der Bearbeitung des Beitrittsanfrage von %(mainchar)s zur Gruppe " "Bei der Bearbeitung des Beitrittsanfrage von %(mainchar)s zur Gruppe "
"%(group)s ist ein unbehandelter Fehler aufgetreten." "%(group)s ist ein unbehandelter Fehler aufgetreten."
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "Beitrittsanfrage von %(mainchar)s zur Gruppe %(group)s abgelehnt." msgstr "Beitrittsanfrage von %(mainchar)s zur Gruppe %(group)s abgelehnt."
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "Austrittsanfrage von %(mainchar)s für Gruppe %(group)s akzeptiert." msgstr "Austrittsanfrage von %(mainchar)s für Gruppe %(group)s akzeptiert."
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1026,42 +1071,42 @@ msgstr ""
"Bei der Bearbeitung des Austrittsanfrage von %(mainchar)s für Gruppe " "Bei der Bearbeitung des Austrittsanfrage von %(mainchar)s für Gruppe "
"%(group)s ist ein unbehandelter Fehler aufgetreten." "%(group)s ist ein unbehandelter Fehler aufgetreten."
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "Austrittsanfrage von %(mainchar)s für Gruppe %(group)s abgelehnt." msgstr "Austrittsanfrage von %(mainchar)s für Gruppe %(group)s abgelehnt."
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "Du kannst dieser Gruppe nicht beitreten" msgstr "Du kannst dieser Gruppe nicht beitreten"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "Du bist bereits Mitglied dieser Gruppe." msgstr "Du bist bereits Mitglied dieser Gruppe."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "Du hast bereits für diese Gruppe angefragt." msgstr "Du hast bereits für diese Gruppe angefragt."
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "Beitritt zur Gruppe %(group)s beantragt." msgstr "Beitritt zur Gruppe %(group)s beantragt."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "Du kannst diese Gruppe nicht verlassen" msgstr "Du kannst diese Gruppe nicht verlassen"
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "Du bist kein Mitglied dieser Gruppe" msgstr "Du bist kein Mitglied dieser Gruppe"
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "Du hast bereits eine ausstehendes Austrittsanfrage für diese Gruppe." msgstr "Du hast bereits eine ausstehendes Austrittsanfrage für diese Gruppe."
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "Austrittsanfrage für Gruppe %(group)s gesendet." msgstr "Austrittsanfrage für Gruppe %(group)s gesendet."
@ -1123,16 +1168,6 @@ msgstr "Erstelle Bewerbung"
msgid "Username" msgid "Username"
msgstr "Benutzername" msgstr "Benutzername"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Aktionen"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1471,10 +1506,6 @@ msgstr "Model"
msgid "Code Name" msgid "Code Name"
msgstr "Code Name" msgstr "Code Name"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Nutzer"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "Status" msgstr "Status"
@ -2218,14 +2249,12 @@ msgstr ""
"Status von %(total)s verarbeiteten Tasks • innerhalb der letzten %(latest)s" "Status von %(total)s verarbeiteten Tasks • innerhalb der letzten %(latest)s"
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr "laufend"
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr "eingereiht"
"\n"
"%(queue_length)s Tasks in der Warteschlange"
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2240,11 +2269,11 @@ msgid "AA Support Discord"
msgstr "AA Support Discord" msgstr "AA Support Discord"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "Nutzermenü" msgstr "Nutzermenü"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "Ausloggen" msgstr "Ausloggen"
@ -2300,22 +2329,30 @@ msgid "Objective"
msgstr "Ziel" msgstr "Ziel"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr "Absoluter Timer"
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr "Datum und Uhrzeit"
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "Tage verbleibend" msgstr "Tage verbleibend"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "Stunden verbleibend" msgstr "Stunden verbleibend"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "Minuten verbleibend" msgstr "Minuten verbleibend"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "Wichtig" msgstr "Wichtig"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "Auf Corp beschränkt" msgstr "Auf Corp beschränkt"

File diff suppressed because it is too large Load Diff

View File

@ -6,18 +6,18 @@
# Translators: # Translators:
# Fegpawn Kaundur, 2023 # Fegpawn Kaundur, 2023
# frank1210 <francolopez_16@hotmail.com>, 2023 # frank1210 <francolopez_16@hotmail.com>, 2023
# trenus, 2023
# Joel Falknau <ozirascal@gmail.com>, 2023
# Young Anexo, 2023 # Young Anexo, 2023
# Joel Falknau <ozirascal@gmail.com>, 2023
# trenus, 2023
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Young Anexo, 2023\n" "Last-Translator: trenus, 2023\n"
"Language-Team: Spanish (https://app.transifex.com/alliance-auth/teams/107430/es/)\n" "Language-Team: Spanish (https://app.transifex.com/alliance-auth/teams/107430/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -33,7 +33,7 @@ msgstr "Google Analytics Universal"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google Analytics V4" msgstr "Google Analytics V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
"Se necesita un personaje principal para realizar esa acción. Añade uno a " "Se necesita un personaje principal para realizar esa acción. Añade uno a "
@ -48,63 +48,68 @@ msgstr "E-mail"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "No puedes añadir o eliminar estos grupos restringidos: %s" msgstr "No puedes añadir o eliminar estos grupos restringidos: %s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "Inglés" msgstr "Inglés"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "Alemán" msgstr "Alemán"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "Español" msgstr "Español"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chino Simplificado" msgstr "Chino Simplificado"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "Ruso" msgstr "Ruso"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "Coreano" msgstr "Coreano"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "Francés" msgstr "Francés"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "Japonés" msgstr "Japonés"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "Italiano" msgstr "Italiano"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "Modo Nocturno" msgstr "Modo Nocturno"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "Estado cambiado a: %s" msgstr "Estado cambiado a: %s"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "El estado de su usuario es ahora: %(state)s" msgstr "El estado de su usuario es ahora: %(state)s"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "Página principal" msgstr "Página principal"
@ -162,8 +167,50 @@ msgstr "Corporación"
msgid "Alliance" msgid "Alliance"
msgstr "Allianza" msgstr "Allianza"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Acciones"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Personaje"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "Ingresar" msgstr "Ingresar"
@ -197,7 +244,7 @@ msgstr "Registrar"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "Enlace de activacion expirado o invalido" msgstr "Enlace de activacion expirado o invalido"
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
@ -206,45 +253,47 @@ msgstr ""
"No se puede cambiar de personaje principal a %(char)s: personaje " "No se puede cambiar de personaje principal a %(char)s: personaje "
"perteneciente a otra cuenta." "perteneciente a otra cuenta."
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "Se ha cambiado tu personaje principal ha %(char)s" msgstr "Se ha cambiado tu personaje principal ha %(char)s"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "Se ha agregado a %(name)s a tu cuenta" msgstr "Se ha agregado a %(name)s a tu cuenta"
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "" msgstr ""
"Se fallo en agregar a %(name)s a tu cuenta: Ya se encuentra registrado en " "Se fallo en agregar a %(name)s a tu cuenta: Ya se encuentra registrado en "
"otra cuenta." "otra cuenta."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "Imposible validar con el personaje seleccionado." "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "El token de registracion expiro." msgstr "El token de registracion expiro."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
msgstr "" msgstr ""
"Confirmacion de mail enviada. Por favor siga el enlace para confirmar " "Confirmacion de mail enviada. Por favor siga el enlace para confirmar "
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "" msgstr ""
"Se ha confirmado su direccion de mail. Por favor igrese su token para " "Se ha confirmado su direccion de mail. Por favor igrese su token para "
"continuar." "continuar."
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "En este momento no se permite el registro de nuevas cuentas." msgstr "En este momento no se permite el registro de nuevas cuentas."
@ -287,19 +336,6 @@ msgstr "Sin registro"
msgid "Last update:" msgid "Last update:"
msgstr "Ultima Actualizacion:" msgstr "Ultima Actualizacion:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Personaje"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -636,19 +672,24 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "Manejo de Grupo" msgstr "Manejo de Grupo"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Usuarios"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "Este nombre ha sido reservado y no puede utilizarse para grupos." msgstr "Este nombre ha sido reservado y no puede utilizarse para grupos."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(auto)" msgstr "(auto)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "Ya existe un grupo con ese nombre." msgstr "Ya existe un grupo con ese nombre."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -658,13 +699,13 @@ msgstr ""
"grupo.<br>Se utiliza para grupos como Miembros, Corp_*, Alliance_*, " "grupo.<br>Se utiliza para grupos como Miembros, Corp_*, Alliance_*, "
"etc.<br><b>Anula las opciones Oculto y Abierto cuando se selecciona.</b>" "etc.<br><b>Anula las opciones Oculto y Abierto cuando se selecciona.</b>"
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
"El grupo está oculto para los usuarios, pero aún pueden unirse con el enlace" "El grupo está oculto para los usuarios, pero aún pueden unirse con el enlace"
" correcto." " correcto."
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -673,7 +714,7 @@ msgstr ""
"soliciten.<br>Si el grupo no está abierto, los usuarios necesitarán que su " "soliciten.<br>Si el grupo no está abierto, los usuarios necesitarán que su "
"solicitud sea aprobada manualmente." "solicitud sea aprobada manualmente."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -685,7 +726,7 @@ msgstr ""
"grupo.<br>Auth no eliminará automáticamente a los usuarios de este grupo " "grupo.<br>Auth no eliminará automáticamente a los usuarios de este grupo "
"cuando ya no estén autenticados." "cuando ya no estén autenticados."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
@ -693,7 +734,7 @@ msgstr ""
"El grupo está restringido. Esto significa que para añadir o eliminar " "El grupo está restringido. Esto significa que para añadir o eliminar "
"usuarios de este grupo se requiere un superusuario administrador." "usuarios de este grupo se requiere un superusuario administrador."
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -703,7 +744,7 @@ msgstr ""
" permiso <code>auth.group_management</code> para permitir que un usuario " " permiso <code>auth.group_management</code> para permitir que un usuario "
"gestione todos los grupos.<br>" "gestione todos los grupos.<br>"
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -713,7 +754,7 @@ msgstr ""
"grupo. Utilice el <code>permiso auth.group_management</code> para permitir " "grupo. Utilice el <code>permiso auth.group_management</code> para permitir "
"que un usuario gestione todos los grupos.<br>" "que un usuario gestione todos los grupos.<br>"
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
@ -721,42 +762,42 @@ msgstr ""
"Los estados que figuren en esta lista podrán unirse a este grupo siempre que" "Los estados que figuren en esta lista podrán unirse a este grupo siempre que"
" dispongan de los permisos adecuados.<br>" " dispongan de los permisos adecuados.<br>"
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
"Breve descripción <i>(máx. 512 caracteres)</i> del grupo que se muestra a " "Breve descripción <i>(máx. 512 caracteres)</i> del grupo que se muestra a "
"los usuarios." "los usuarios."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "Se pueden solicitar grupos no públicos" msgstr "Se pueden solicitar grupos no públicos"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "nombre" msgstr "nombre"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "Nombre que no se puede utilizar para los grupos." msgstr "Nombre que no se puede utilizar para los grupos."
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "razón" msgstr "razón"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "Razón por la que este nombre está reservado." msgstr "Razón por la que este nombre está reservado."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "creado por" msgstr "creado por"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "creado en" msgstr "creado en"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "Fecha de creación de esta entrada" msgstr "Fecha de creación de esta entrada"
@ -983,26 +1024,26 @@ msgstr "Solicitudes de Grupo"
msgid "Group Membership" msgid "Group Membership"
msgstr "Membresia de Grupo" msgstr "Membresia de Grupo"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "El usuario %(user)s fue removido del grupo %(group)s" msgstr "El usuario %(user)s fue removido del grupo %(group)s"
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "El usuario no existe en ese grupos" msgstr "El usuario no existe en ese grupos"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "El grupo no existe" msgstr "El grupo no existe"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "Solicitud aceptada de %(mainchar)s a %(group)s" msgstr "Solicitud aceptada de %(mainchar)s a %(group)s"
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1011,18 +1052,18 @@ msgstr ""
"Ocurrio un error cuando se intento procesar la informacion de %(mainchar)s " "Ocurrio un error cuando se intento procesar la informacion de %(mainchar)s "
"al grupo %(group)s." "al grupo %(group)s."
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "Se rechazo la solicitud de %(mainchar)s al grupo %(group)s." msgstr "Se rechazo la solicitud de %(mainchar)s al grupo %(group)s."
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "Se acepto la solicitud de %(mainchar)s para dejar el grupo %(group)s." msgstr "Se acepto la solicitud de %(mainchar)s para dejar el grupo %(group)s."
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1031,43 +1072,43 @@ msgstr ""
"Se ha producido un error al procesar la solicitud de %(mainchar)s para " "Se ha producido un error al procesar la solicitud de %(mainchar)s para "
"abandonar %(group)s." "abandonar %(group)s."
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "" msgstr ""
"Se rechazo la solicitud de %(mainchar)s para dejar el grupo %(group)s." "Se rechazo la solicitud de %(mainchar)s para dejar el grupo %(group)s."
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "No puedes unirte a ese grupo" msgstr "No puedes unirte a ese grupo"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "Ya eres miembro de ese grupo." msgstr "Ya eres miembro de ese grupo."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "Ya tiene una solicitud pendiente para ese grupo." msgstr "Ya tiene una solicitud pendiente para ese grupo."
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "Solicitud enviada al grupo %(group)s." msgstr "Solicitud enviada al grupo %(group)s."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "No puedes dejar el grupos" msgstr "No puedes dejar el grupos"
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "No eres miembro de ese grupo" msgstr "No eres miembro de ese grupo"
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "Ya tiene una solicitud de baja pendiente para ese grupo." msgstr "Ya tiene una solicitud de baja pendiente para ese grupo."
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "Solicitaste dejar el grupo %(group)s." msgstr "Solicitaste dejar el grupo %(group)s."
@ -1129,16 +1170,6 @@ msgstr "Crear Solicitud"
msgid "Username" msgid "Username"
msgstr "Usuario" msgstr "Usuario"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Acciones"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1477,10 +1508,6 @@ msgstr "Modelo"
msgid "Code Name" msgid "Code Name"
msgstr "Nombre codigo" msgstr "Nombre codigo"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Usuarios"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "Estados" msgstr "Estados"
@ -2219,14 +2246,12 @@ msgstr ""
"Estado de %(total)s tareas procesadas • últimos %(latest)s" "Estado de %(total)s tareas procesadas • últimos %(latest)s"
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
"\n"
"%(queue_length)s tareas en cola"
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2241,11 +2266,11 @@ msgid "AA Support Discord"
msgstr "Soporte Discord AA" msgstr "Soporte Discord AA"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "Menú de usuario" msgstr "Menú de usuario"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "Salir" msgstr "Salir"
@ -2301,22 +2326,30 @@ msgid "Objective"
msgstr "Objetivo" msgstr "Objetivo"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "Dias restantes" msgstr "Dias restantes"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "Horas Restantes" msgstr "Horas Restantes"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "Minutos Restantes" msgstr "Minutos Restantes"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "Importante" msgstr "Importante"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "Restringido a Corp" msgstr "Restringido a Corp"

View File

@ -4,23 +4,23 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Keven D. <theenarki@gmail.com>, 2023 # Mickael Gr4vity, 2023
# rockclodbuster, 2023
# Geoffrey Fabbro, 2023
# Mohssine Daghghar, 2023
# François LACROIX-DURANT <umbre@fallenstarscreations.com>, 2023
# Mickael PATTE, 2023
# Philippe Querin-Laporte <philippe.querin@hotmail.com>, 2023
# Idea ., 2023 # Idea ., 2023
# rockclodbuster, 2023
# Keven D. <theenarki@gmail.com>, 2023
# Mohssine Daghghar, 2023
# Philippe Querin-Laporte <philippe.querin@hotmail.com>, 2023
# draktanar KarazGrong <umbre@fallenstarscreations.com>, 2023
# Geoffrey Fabbro, 2023
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Idea ., 2023\n" "Last-Translator: Geoffrey Fabbro, 2023\n"
"Language-Team: French (France) (https://app.transifex.com/alliance-auth/teams/107430/fr_FR/)\n" "Language-Team: French (France) (https://app.transifex.com/alliance-auth/teams/107430/fr_FR/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -36,7 +36,7 @@ msgstr "Google Analytique Universelle"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google Analytics V4" msgstr "Google Analytics V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
"Un personnage principal est nécessaire pour effectuer cette action. Ajoutez-" "Un personnage principal est nécessaire pour effectuer cette action. Ajoutez-"
@ -53,63 +53,68 @@ msgstr ""
"Vous n'avez pas lautorisation d'ajouter ou d'enlever ces groupes " "Vous n'avez pas lautorisation d'ajouter ou d'enlever ces groupes "
"restreints: %s" "restreints: %s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "Anglais" msgstr "Anglais"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "Allemand" msgstr "Allemand"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "Espagnol" msgstr "Espagnol"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinois simplifié" msgstr "Chinois simplifié"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "Russe" msgstr "Russe"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "Coréen" msgstr "Coréen"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "Français" msgstr "Français"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "Japonais" msgstr "Japonais"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "Italien" msgstr "Italien"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "Langue" msgstr "Langue"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "Mode Nuit" msgstr "Mode Nuit"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "État changé à: %s" msgstr "État changé à: %s"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "L'état de votre personnage est maintenant: %(state)s" msgstr "L'état de votre personnage est maintenant: %(state)s"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "Écran de bord" msgstr "Écran de bord"
@ -167,8 +172,50 @@ msgstr "Corpo"
msgid "Alliance" msgid "Alliance"
msgstr "Alliance" msgstr "Alliance"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Actions"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Personnage"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "Connexion" msgstr "Connexion"
@ -202,7 +249,7 @@ msgstr "S'inscrire"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "Lien d'activation invalide ou expiré." msgstr "Lien d'activation invalide ou expiré."
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
@ -211,30 +258,32 @@ msgstr ""
"Impossible de changer le personnage principal à %(char)s. Le personnage " "Impossible de changer le personnage principal à %(char)s. Le personnage "
"appartient à un autre compte." "appartient à un autre compte."
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "Changé le personnage principal à %(char)s" msgstr "Changé le personnage principal à %(char)s"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "Ajouté %(name)s à votre compte." msgstr "Ajouté %(name)s à votre compte."
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "Impossible d'ajouter %(name)s à votre compte: ils ont déjà un compte." msgstr "Impossible d'ajouter %(name)s à votre compte: ils ont déjà un compte."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "Personnage principal : échec de l'identification." "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "Le token d'enregistrement est expiré." msgstr "Le token d'enregistrement est expiré."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
@ -242,12 +291,12 @@ msgstr ""
"Email de confirmation envoyé. Cliquez sur le lien pour valider votre adresse" "Email de confirmation envoyé. Cliquez sur le lien pour valider votre adresse"
" email." " email."
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "" msgstr ""
"Votre adresse email a été confirmé. Veuillez vous connecter pour continuer." "Votre adresse email a été confirmé. Veuillez vous connecter pour continuer."
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "La création de nouveaux comptes n'est pas actuellement permise." msgstr "La création de nouveaux comptes n'est pas actuellement permise."
@ -290,19 +339,6 @@ msgstr "Pas inscrit"
msgid "Last update:" msgid "Last update:"
msgstr "Dernière mise à jour:" msgstr "Dernière mise à jour:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Personnage"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -639,19 +675,24 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "Gestion de groupe" msgstr "Gestion de groupe"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Utilisateurs"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "Ce nom a été réserver et il ne peut être utilisé pour les groupes." msgstr "Ce nom a été réserver et il ne peut être utilisé pour les groupes."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(automatique)" msgstr "(automatique)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "Il existe déjà un groupe portant ce nom." msgstr "Il existe déjà un groupe portant ce nom."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -662,13 +703,13 @@ msgstr ""
"Corporations _*, Alliance etc.<br><b> Annule les options masquer et exposer " "Corporations _*, Alliance etc.<br><b> Annule les options masquer et exposer "
"quand sélectionner." "quand sélectionner."
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
"Le groupe est caché aux utilisateurs, mais ils peuvent toujours rejoindre " "Le groupe est caché aux utilisateurs, mais ils peuvent toujours rejoindre "
"avec le bon lien." "avec le bon lien."
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -677,7 +718,7 @@ msgstr ""
" demande. <br> Si le groupe nest pas ouvert, les utilisateurs auront besoin" " demande. <br> Si le groupe nest pas ouvert, les utilisateurs auront besoin"
" que leurs demandes soit approuvées manuellement." " que leurs demandes soit approuvées manuellement."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -689,7 +730,7 @@ msgstr ""
"groupe.<br> L' Auth ne supprimera pas automatiquement les utilisateurs de ce" "groupe.<br> L' Auth ne supprimera pas automatiquement les utilisateurs de ce"
" groupe lorsquils ne seront plus authentifiés." " groupe lorsquils ne seront plus authentifiés."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
@ -697,62 +738,62 @@ msgstr ""
"Le groupe est restreint. Cela signifie que lajout ou la suppression " "Le groupe est restreint. Cela signifie que lajout ou la suppression "
"dutilisateurs pour ce groupe nécessite un administrateur superutilisateur." "dutilisateurs pour ce groupe nécessite un administrateur superutilisateur."
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
"groups.<br>" "groups.<br>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
"groups.<br>" "groups.<br>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
"Brève description <i> (512 caractères maximum) </i> du groupe présenté aux " "Brève description <i> (512 caractères maximum) </i> du groupe présenté aux "
"utilisateurs." "utilisateurs."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "Peut demander des groupes non publics" msgstr "Peut demander des groupes non publics"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "Nom" msgstr "Nom"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "Nom qui ne peut pas être utilisé pour les groupes." msgstr "Nom qui ne peut pas être utilisé pour les groupes."
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "raison" msgstr "raison"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "Raison pour laquelle ce nom est réservé." msgstr "Raison pour laquelle ce nom est réservé."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "créé par" msgstr "créé par"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "créé à" msgstr "créé à"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "Date de création de cette entrée" msgstr "Date de création de cette entrée"
@ -979,26 +1020,26 @@ msgstr "Demandes de groupe"
msgid "Group Membership" msgid "Group Membership"
msgstr "Groupe appartenance " msgstr "Groupe appartenance "
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "L'utilisateur %(user)s à été retiré du groupe %(group)s." msgstr "L'utilisateur %(user)s à été retiré du groupe %(group)s."
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "L'utilisateur n'existe pas dans ce groupe" msgstr "L'utilisateur n'existe pas dans ce groupe"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "Groupe non-existant" msgstr "Groupe non-existant"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "Candidature de %(mainchar)s acceptée à %(group)s." msgstr "Candidature de %(mainchar)s acceptée à %(group)s."
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1007,18 +1048,18 @@ msgstr ""
"Une erreur inattendue est survenue durant le traitement de l'application de " "Une erreur inattendue est survenue durant le traitement de l'application de "
"%(mainchar)s à %(group)s." "%(mainchar)s à %(group)s."
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "L'application de %(mainchar)s à %(group)s est rejetée." msgstr "L'application de %(mainchar)s à %(group)s est rejetée."
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "La demande de retirer %(mainchar)s de %(group)s est acceptée." msgstr "La demande de retirer %(mainchar)s de %(group)s est acceptée."
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1027,42 +1068,42 @@ msgstr ""
"Une erreur inattendue est survenue durant le traitement de la demande de " "Une erreur inattendue est survenue durant le traitement de la demande de "
"retirer %(mainchar)s de %(group)s." "retirer %(mainchar)s de %(group)s."
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "La remande de retirer %(mainchar)s de %(group)s a été refusée." msgstr "La remande de retirer %(mainchar)s de %(group)s a été refusée."
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "Vous ne pouvez pas joindre ce groupe." msgstr "Vous ne pouvez pas joindre ce groupe."
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "Vous faites déjà parti de ce groupe." msgstr "Vous faites déjà parti de ce groupe."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "Vous avez déjà une application en attente pour joindre ce groupe." msgstr "Vous avez déjà une application en attente pour joindre ce groupe."
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "Appliqué au groupe %(group)s." msgstr "Appliqué au groupe %(group)s."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "Vous ne pouvez pas quitter ce groupe." msgstr "Vous ne pouvez pas quitter ce groupe."
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "Vous n'êtes pas un membre de ce groupe." msgstr "Vous n'êtes pas un membre de ce groupe."
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "Vous avec déjà une demande de quitter ce groupe en attente." msgstr "Vous avec déjà une demande de quitter ce groupe en attente."
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "Appliqué pour quitter le groupe %(group)s." msgstr "Appliqué pour quitter le groupe %(group)s."
@ -1124,16 +1165,6 @@ msgstr "Créer une application"
msgid "Username" msgid "Username"
msgstr "Nom d'utilisateur" msgstr "Nom d'utilisateur"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Actions"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1472,10 +1503,6 @@ msgstr "Modèle"
msgid "Code Name" msgid "Code Name"
msgstr "Nom De Code" msgstr "Nom De Code"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Utilisateurs"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "États" msgstr "États"
@ -2217,15 +2244,12 @@ msgstr ""
" " " "
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
"\n"
" %(queue_length)stâches en file d'attente\n"
" "
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2240,11 +2264,11 @@ msgid "AA Support Discord"
msgstr "Support Discord AA" msgstr "Support Discord AA"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "Menu Utilisateur" msgstr "Menu Utilisateur"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "Déconnexion" msgstr "Déconnexion"
@ -2300,22 +2324,30 @@ msgid "Objective"
msgstr "Objectif" msgstr "Objectif"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "Jour restants" msgstr "Jour restants"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "Heures restantes" msgstr "Heures restantes"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "Minutes restantes" msgstr "Minutes restantes"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "Important" msgstr "Important"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "Limité à la Corporation" msgstr "Limité à la Corporation"

View File

@ -12,8 +12,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Linus Hope, 2023\n" "Last-Translator: Linus Hope, 2023\n"
"Language-Team: Italian (Italy) (https://app.transifex.com/alliance-auth/teams/107430/it_IT/)\n" "Language-Team: Italian (Italy) (https://app.transifex.com/alliance-auth/teams/107430/it_IT/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -30,7 +30,7 @@ msgstr "Google Analytics Universal"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google Analytics V4" msgstr "Google Analytics V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
"Per completare questa azione è necessario un personaggio principale. " "Per completare questa azione è necessario un personaggio principale. "
@ -45,63 +45,68 @@ msgstr "Indirizzo di posta elettronica"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "Non ti è consentito aggiungere o rimuovere questi gruppi limitati:%s" msgstr "Non ti è consentito aggiungere o rimuovere questi gruppi limitati:%s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "Inglese" msgstr "Inglese"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "Tedesco" msgstr "Tedesco"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "Spagnolo" msgstr "Spagnolo"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Cinese semplificato" msgstr "Cinese semplificato"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "Russo" msgstr "Russo"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "Coreano" msgstr "Coreano"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "Francese" msgstr "Francese"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "Giapponese" msgstr "Giapponese"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "Italiano" msgstr "Italiano"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "Lingua" msgstr "Lingua"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "Modalità scura" msgstr "Modalità scura"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "Stato modificato a: %s" msgstr "Stato modificato a: %s"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "Il tuo stato utente è ora: %(state)s" msgstr "Il tuo stato utente è ora: %(state)s"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "Pannello di controllo" msgstr "Pannello di controllo"
@ -160,8 +165,50 @@ msgstr "Corporazione"
msgid "Alliance" msgid "Alliance"
msgstr "Alleanza" msgstr "Alleanza"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Azioni"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Personaggio"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "Accedi" msgstr "Accedi"
@ -195,7 +242,7 @@ msgstr "Registrati"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "Il link di attivazione è invalido o scaduto." msgstr "Il link di attivazione è invalido o scaduto."
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
@ -204,32 +251,34 @@ msgstr ""
"Il seguente personaggio %(char)s non può essere reso principale: è già " "Il seguente personaggio %(char)s non può essere reso principale: è già "
"utilizzato da un altro profilo." "utilizzato da un altro profilo."
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "Hai reso personaggio principale: %(char)s" msgstr "Hai reso personaggio principale: %(char)s"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "%(name)s è stato aggiunto al tuo profilo." msgstr "%(name)s è stato aggiunto al tuo profilo."
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "" msgstr ""
"Impossibile aggiungere %(name)s al tuo profilo: quel personaggio è già " "Impossibile aggiungere %(name)s al tuo profilo: quel personaggio è già "
"collegato ad un altro profilo." "collegato ad un altro profilo."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "Impossibile autenticarsi con il personaggio selezionato." "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "Il token di registrazione è scaduto." msgstr "Il token di registrazione è scaduto."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
@ -237,13 +286,13 @@ msgstr ""
"Una e-mail di conferma è stata inviata. Per favore, utilizza il link per " "Una e-mail di conferma è stata inviata. Per favore, utilizza il link per "
"confermare il tuo indirizzo di posta elettronica." "confermare il tuo indirizzo di posta elettronica."
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "" msgstr ""
"Il tuo indirizzo di posta elettronica è stato confermato. Per favore accedi " "Il tuo indirizzo di posta elettronica è stato confermato. Per favore accedi "
"per continuare." "per continuare."
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "Al momento non è possibile registrare nuovi profili." msgstr "Al momento non è possibile registrare nuovi profili."
@ -286,19 +335,6 @@ msgstr "Non registrati"
msgid "Last update:" msgid "Last update:"
msgstr "Ultimo aggiornamento:" msgstr "Ultimo aggiornamento:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Personaggio"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -638,19 +674,24 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "Gestione gruppi" msgstr "Gestione gruppi"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Utenti"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "Questo nome è riservato e non può essere utilizzato per gruppi." msgstr "Questo nome è riservato e non può essere utilizzato per gruppi."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(auto)" msgstr "(auto)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "Esiste già un gruppo con quel nome." msgstr "Esiste già un gruppo con quel nome."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -661,13 +702,13 @@ msgstr ""
"Alleanze, etc. <br><b>Sovrascrive opzioni nascoste e aperte quando " "Alleanze, etc. <br><b>Sovrascrive opzioni nascoste e aperte quando "
"seleazionato." "seleazionato."
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
"Il gruppo è nascosto agli utenti ma questi vi possono aderire utilizzando il" "Il gruppo è nascosto agli utenti ma questi vi possono aderire utilizzando il"
" link corretto. " " link corretto. "
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -676,7 +717,7 @@ msgstr ""
"di richiesta. <br>Se il gruppo non è aperto gli utenti necessiteranno di " "di richiesta. <br>Se il gruppo non è aperto gli utenti necessiteranno di "
"approvazione manuale." "approvazione manuale."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -688,7 +729,7 @@ msgstr ""
"gruppo. <br>Auth non rimuoverà automaticamente gli utenti da questo gruppo " "gruppo. <br>Auth non rimuoverà automaticamente gli utenti da questo gruppo "
"quando non sono più autenticati." "quando non sono più autenticati."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
@ -696,7 +737,7 @@ msgstr ""
"Il gruppo è limitato. Ciò significa che l'aggiunta o la rimozione di utenti " "Il gruppo è limitato. Ciò significa che l'aggiunta o la rimozione di utenti "
"per questo gruppo richiede un amministratore." "per questo gruppo richiede un amministratore."
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -706,7 +747,7 @@ msgstr ""
"l'<code>autorizzazione auth.group_management </code> per consentire ad un " "l'<code>autorizzazione auth.group_management </code> per consentire ad un "
"utente di gestire tutti i gruppi. <br> " "utente di gestire tutti i gruppi. <br> "
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -716,7 +757,7 @@ msgstr ""
" Usa l'<code>autorizzazione auth.group_management </code> per consentire ad " " Usa l'<code>autorizzazione auth.group_management </code> per consentire ad "
"un utente di gestire tutti i gruppi. <br> " "un utente di gestire tutti i gruppi. <br> "
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
@ -724,42 +765,42 @@ msgstr ""
"Gli stati qui elencati avranno la possibilità di aderire a questo gruppo a " "Gli stati qui elencati avranno la possibilità di aderire a questo gruppo a "
"condizione che dispongano delle opportune autorizzazioni. <br> " "condizione che dispongano delle opportune autorizzazioni. <br> "
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
"Breve descrizione <i>(max. 512 caratteri)</i> del gruppo da mostrare agli " "Breve descrizione <i>(max. 512 caratteri)</i> del gruppo da mostrare agli "
"utenti." "utenti."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "Può fare richiesta a gruppi non pubblici" msgstr "Può fare richiesta a gruppi non pubblici"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "nome" msgstr "nome"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "Il nome scelto non può essere utilizzato per gruppi." msgstr "Il nome scelto non può essere utilizzato per gruppi."
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "ragione" msgstr "ragione"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "Ragione per la quale questo nome è riservato." msgstr "Ragione per la quale questo nome è riservato."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "creato da" msgstr "creato da"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "creato il" msgstr "creato il"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "Data in cui è stata creata questa voce." msgstr "Data in cui è stata creata questa voce."
@ -986,26 +1027,26 @@ msgstr "Richieste"
msgid "Group Membership" msgid "Group Membership"
msgstr "Membri del gruppo" msgstr "Membri del gruppo"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "Rimosso il membro %(user)s dal gruppo %(group)s." msgstr "Rimosso il membro %(user)s dal gruppo %(group)s."
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "Lutente non fa parte del gruppo selezionato" msgstr "Lutente non fa parte del gruppo selezionato"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "Il gruppo non esiste" msgstr "Il gruppo non esiste"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "La domanda di %(mainchar)s per %(group)s è stata accettata." msgstr "La domanda di %(mainchar)s per %(group)s è stata accettata."
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1014,20 +1055,20 @@ msgstr ""
"Si è verificato unerrore durante lelaborazione della domanda di " "Si è verificato unerrore durante lelaborazione della domanda di "
"%(mainchar)s per %(group)s." "%(mainchar)s per %(group)s."
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "La domanda di %(mainchar)s per %(group)s è stata rifiutata." msgstr "La domanda di %(mainchar)s per %(group)s è stata rifiutata."
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "" msgstr ""
"La domanda di congedo da parte di %(mainchar)s per %(group)s è stata " "La domanda di congedo da parte di %(mainchar)s per %(group)s è stata "
"accettata." "accettata."
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1036,45 +1077,45 @@ msgstr ""
"Si è verificato unerrore durante lelaborazione della domanda di congedo da" "Si è verificato unerrore durante lelaborazione della domanda di congedo da"
" parte di %(mainchar)s per %(group)s." " parte di %(mainchar)s per %(group)s."
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "" msgstr ""
"La domanda di congedo da parte di %(mainchar)s per %(group)s è stata " "La domanda di congedo da parte di %(mainchar)s per %(group)s è stata "
"rifiutata." "rifiutata."
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "Non puoi unirti a questo gruppo" msgstr "Non puoi unirti a questo gruppo"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "Fai già parte del gruppo selezionato." msgstr "Fai già parte del gruppo selezionato."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "" msgstr ""
"Hai già una candidatura in attesa di essere elaborata per questo gruppo" "Hai già una candidatura in attesa di essere elaborata per questo gruppo"
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "Hai fatto domanda per il gruppo %(group)s." msgstr "Hai fatto domanda per il gruppo %(group)s."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "Non puoi lasciare questo gruppo." msgstr "Non puoi lasciare questo gruppo."
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "Non sei un membro di questo gruppo." msgstr "Non sei un membro di questo gruppo."
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "Hai già una richiesta di congedo in sospeso per quel gruppo." msgstr "Hai già una richiesta di congedo in sospeso per quel gruppo."
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "Hai fatto domanda di congedo per %(group)s." msgstr "Hai fatto domanda di congedo per %(group)s."
@ -1136,16 +1177,6 @@ msgstr "Crea una domanda"
msgid "Username" msgid "Username"
msgstr "Nome utente" msgstr "Nome utente"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Azioni"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1484,10 +1515,6 @@ msgstr "Modello"
msgid "Code Name" msgid "Code Name"
msgstr "Nome del codice" msgstr "Nome del codice"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Utenti"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "Stati" msgstr "Stati"
@ -2233,14 +2260,12 @@ msgstr ""
" " " "
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
"\n"
"%(queue_length)scompiti in coda"
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2255,11 +2280,11 @@ msgid "AA Support Discord"
msgstr "AA Discord di supporto" msgstr "AA Discord di supporto"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "Menu utente" msgstr "Menu utente"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "Disconnettersi" msgstr "Disconnettersi"
@ -2315,22 +2340,30 @@ msgid "Objective"
msgstr "Obiettivo" msgstr "Obiettivo"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "Giorni rimanenti" msgstr "Giorni rimanenti"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "Ore rimanenti" msgstr "Ore rimanenti"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "Minuti rimanenti " msgstr "Minuti rimanenti "
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "Importante" msgstr "Importante"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "Limitato alla corporazione" msgstr "Limitato alla corporazione"

View File

@ -4,8 +4,8 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Foch Petain <brigadier.rockforward@gmail.com>, 2023
# kotaneko, 2023 # kotaneko, 2023
# Foch Petain <brigadier.rockforward@gmail.com>, 2023
# Joel Falknau <ozirascal@gmail.com>, 2023 # Joel Falknau <ozirascal@gmail.com>, 2023
# #
#, fuzzy #, fuzzy
@ -13,8 +13,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n" "Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n"
"Language-Team: Japanese (https://app.transifex.com/alliance-auth/teams/107430/ja/)\n" "Language-Team: Japanese (https://app.transifex.com/alliance-auth/teams/107430/ja/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -31,7 +31,7 @@ msgstr "Google ユニバーサル アナリティクス"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google アナリティクス 4" msgstr "Google アナリティクス 4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "実行するためにはメインキャラクターの設定が必要です。設定してください。" msgstr "実行するためにはメインキャラクターの設定が必要です。設定してください。"
@ -44,63 +44,68 @@ msgstr "メールアドレス"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "これらの制限付きグループを追加または削除することはできません。%s" msgstr "これらの制限付きグループを追加または削除することはできません。%s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "英語" msgstr "英語"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "ドイツ語" msgstr "ドイツ語"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "スペイン語" msgstr "スペイン語"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "中国語 簡体字" msgstr "中国語 簡体字"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "ロシア語" msgstr "ロシア語"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "韓国語" msgstr "韓国語"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "フランス語" msgstr "フランス語"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "日本語" msgstr "日本語"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "イタリア語" msgstr "イタリア語"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "言語" msgstr "言語"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "ナイトモード" msgstr "ナイトモード"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "分類が%sに変更されました。" msgstr "分類が%sに変更されました。"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "あなたの分類は%(state)sになりました。" msgstr "あなたの分類は%(state)sになりました。"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "ダッシュボード" msgstr "ダッシュボード"
@ -158,8 +163,50 @@ msgstr "Corp"
msgid "Alliance" msgid "Alliance"
msgstr "Alliance" msgstr "Alliance"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "アクション"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "キャラクター"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "ログイン" msgstr "ログイン"
@ -191,47 +238,49 @@ msgstr "登録"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "アクティベーションリンクが無効か期限切れです。" msgstr "アクティベーションリンクが無効か期限切れです。"
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
"account." "account."
msgstr "メインキャラクターを%(char)sへ変更できません。別のアカウントによって利用されています。" msgstr "メインキャラクターを%(char)sへ変更できません。別のアカウントによって利用されています。"
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "メインキャラクターを%(char)sへ変更しました。" msgstr "メインキャラクターを%(char)sへ変更しました。"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "%(name)sをアカウントに追加しました。" msgstr "%(name)sをアカウントに追加しました。"
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "%(name)sをアカウントに追加することができません。すでに他のアカウントを持っています。" msgstr "%(name)sをアカウントに追加することができません。すでに他のアカウントを持っています。"
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "選択されたキャラクターの認証が行えませんでした。" "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "Registrationトークンが有効期限切れです。" msgstr "Registrationトークンが有効期限切れです。"
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
msgstr "確認のメールを送信しました。メール内のリンクをご確認の上、メールアドレスの認証を完了させてください。" msgstr "確認のメールを送信しました。メール内のリンクをご確認の上、メールアドレスの認証を完了させてください。"
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "メールアドレスを確認しました。続行するにはログインしてください。" msgstr "メールアドレスを確認しました。続行するにはログインしてください。"
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "新規アカウントの登録は、現時点ではできません。" msgstr "新規アカウントの登録は、現時点ではできません。"
@ -274,19 +323,6 @@ msgstr "未登録"
msgid "Last update:" msgid "Last update:"
msgstr "最終更新:" msgstr "最終更新:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "キャラクター"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -616,19 +652,24 @@ msgstr "{character.character_name} のフリート参加を登録できません
msgid "Group Management" msgid "Group Management"
msgstr "グループ管理" msgstr "グループ管理"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "ユーザ"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "この名前は予約済みで、グループには使用できません。" msgstr "この名前は予約済みで、グループには使用できません。"
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(auto)" msgstr "(auto)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "その名前のグループが既に存在しています。" msgstr "その名前のグループが既に存在しています。"
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -637,18 +678,18 @@ msgstr ""
"内部グループです。ユーザーはこのグループを表示したり、参加したり、参加をリクエストしたりすることはできません。<br>Members、Corp_*、Alliance_*などのグループに使用します。選択すると、非表示" "内部グループです。ユーザーはこのグループを表示したり、参加したり、参加をリクエストしたりすることはできません。<br>Members、Corp_*、Alliance_*などのグループに使用します。選択すると、非表示"
" オプションと 開く <br> <b> オプションが上書きされます。</b> " " オプションと 開く <br> <b> オプションが上書きされます。</b> "
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "グループはユーザーに表示されませんが、正しいリンク経由で参加することができます。" msgstr "グループはユーザーに表示されませんが、正しいリンク経由で参加することができます。"
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
msgstr "" msgstr ""
"グループはオープンで、ユーザーはリクエストに応じて自動的に追加されます。<br>グループがオープンでない場合、ユーザーのリクエストは手動で承認する必要があります。" "グループはオープンで、ユーザーはリクエストに応じて自動的に追加されます。<br>グループがオープンでない場合、ユーザーのリクエストは手動で承認する必要があります。"
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -658,13 +699,13 @@ msgstr ""
"グループは一般公開されています。登録ユーザーなら誰でもこのグループに参加でき、このグループに設定されている他のオプションに基づいて表示されます。<br>認証されなくなっても、Auth" "グループは一般公開されています。登録ユーザーなら誰でもこのグループに参加でき、このグループに設定されている他のオプションに基づいて表示されます。<br>認証されなくなっても、Auth"
" はユーザーをこのグループから自動的に削除しません。" " はユーザーをこのグループから自動的に削除しません。"
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
msgstr "グループは参加が制限されています。つまり、このグループのユーザーを追加または削除するには、スーパーユーザー管理者が必要です。" msgstr "グループは参加が制限されています。つまり、このグループのユーザーを追加または削除するには、スーパーユーザー管理者が必要です。"
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -673,7 +714,7 @@ msgstr ""
"グループリーダーは、このグループのリクエストを処理できます。<code>auth.group_management </code> " "グループリーダーは、このグループのリクエストを処理できます。<code>auth.group_management </code> "
"権限を使用して、ユーザーがすべてのグループを管理できるようにします。<br> " "権限を使用して、ユーザーがすべてのグループを管理できるようにします。<br> "
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -682,46 +723,46 @@ msgstr ""
"リーダーグループのメンバーは、このグループのリクエストを処理できます。<code>auth.group_management </code> " "リーダーグループのメンバーは、このグループのリクエストを処理できます。<code>auth.group_management </code> "
"権限を使用して、ユーザーがすべてのグループを管理できるようにします。<br> " "権限を使用して、ユーザーがすべてのグループを管理できるようにします。<br> "
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
msgstr "ここに記載されているStatesは、適切な許可があれば、このグループに参加できます。<br> " msgstr "ここに記載されているStatesは、適切な許可があれば、このグループに参加できます。<br> "
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "ユーザーに表示されるグループの簡単な説明 <i> (最大 512 文字) </i>。" msgstr "ユーザーに表示されるグループの簡単な説明 <i> (最大 512 文字) </i>。"
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "非公開グループをリクエストできる" msgstr "非公開グループをリクエストできる"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "名前" msgstr "名前"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "グループには使用できない名前。" msgstr "グループには使用できない名前。"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "理由" msgstr "理由"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "この名前が使用予約されている理由。" msgstr "この名前が使用予約されている理由。"
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "作成者" msgstr "作成者"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "作成日時" msgstr "作成日時"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "このエントリが作成された日付" msgstr "このエントリが作成された日付"
@ -948,86 +989,86 @@ msgstr "グループリクエスト"
msgid "Group Membership" msgid "Group Membership"
msgstr "グループメンバーシップ" msgstr "グループメンバーシップ"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "%(user)sを%(group)sから削除する。" msgstr "%(user)sを%(group)sから削除する。"
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "誰もグループに参加してません。" msgstr "誰もグループに参加してません。"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "グループが存在しません。" msgstr "グループが存在しません。"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)sからの%(group)sへの参加申請を承認しました。" msgstr "%(mainchar)sからの%(group)sへの参加申請を承認しました。"
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
"%(mainchar)s to %(group)s." "%(mainchar)s to %(group)s."
msgstr "%(mainchar)sからの%(group)sへの参加申請を処理中にエラーが発生しました。" msgstr "%(mainchar)sからの%(group)sへの参加申請を処理中にエラーが発生しました。"
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)sからの%(group)sへの参加申請は拒否されました。" msgstr "%(mainchar)sからの%(group)sへの参加申請は拒否されました。"
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)sからの%(group)sからの脱退申請は承認されました。" msgstr "%(mainchar)sからの%(group)sからの脱退申請は承認されました。"
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
"%(mainchar)s to leave %(group)s." "%(mainchar)s to leave %(group)s."
msgstr "%(mainchar)sからの%(group)sからの脱退申請を処理中にエラーが発生しました。" msgstr "%(mainchar)sからの%(group)sからの脱退申請を処理中にエラーが発生しました。"
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)sの%(group)sからの脱退申請は拒否されました。" msgstr "%(mainchar)sの%(group)sからの脱退申請は拒否されました。"
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "このGroupには入れません" msgstr "このGroupには入れません"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "すでにその Group に参加してます。" msgstr "すでにその Group に参加してます。"
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "すでに参加申請を送付済みです。" msgstr "すでに参加申請を送付済みです。"
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "%(group)sへの参加申請を送信しました。" msgstr "%(group)sへの参加申請を送信しました。"
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "この Group から脱退することはできません" msgstr "この Group から脱退することはできません"
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "あなたはその Group のメンバーではありません" msgstr "あなたはその Group のメンバーではありません"
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "すでに脱退申請を送信済みです。" msgstr "すでに脱退申請を送信済みです。"
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "%(group)sからの脱退申請を送信しました。" msgstr "%(group)sからの脱退申請を送信しました。"
@ -1089,16 +1130,6 @@ msgstr "申請を作成"
msgid "Username" msgid "Username"
msgstr "ユーザー名" msgstr "ユーザー名"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "アクション"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1437,10 +1468,6 @@ msgstr "モデル"
msgid "Code Name" msgid "Code Name"
msgstr "コードネーム" msgstr "コードネーム"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "ユーザ"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "States" msgstr "States"
@ -2171,15 +2198,12 @@ msgstr ""
" " " "
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
"\n"
" %(queue_length)sキューに入っているタスク\n"
" "
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2194,11 +2218,11 @@ msgid "AA Support Discord"
msgstr "AA サポートディスコード" msgstr "AA サポートディスコード"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "ユーザーメニュー" msgstr "ユーザーメニュー"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "ログアウト" msgstr "ログアウト"
@ -2254,22 +2278,30 @@ msgid "Objective"
msgstr "目標" msgstr "目標"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "残り日数" msgstr "残り日数"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "残り時間" msgstr "残り時間"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "残り分数" msgstr "残り分数"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "重要" msgstr "重要"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "コーポレーション制限付き" msgstr "コーポレーション制限付き"

View File

@ -4,22 +4,22 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# None None <khd1226543@gmail.com>, 2023
# Joel Falknau <ozirascal@gmail.com>, 2023 # Joel Falknau <ozirascal@gmail.com>, 2023
# Seowon Jung <seowon@hawaii.edu>, 2023 # None None <khd1226543@gmail.com>, 2023
# Olgeda Choi <undead.choi@gmail.com>, 2023
# ThatRagingKid, 2023 # ThatRagingKid, 2023
# Lahty <js03js70@gmail.com>, 2023 # Lahty <js03js70@gmail.com>, 2023
# jackfrost, 2023 # Olgeda Choi <undead.choi@gmail.com>, 2023
# Seowon Jung <seowon@hawaii.edu>, 2023
# Alpha, 2023
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: jackfrost, 2023\n" "Last-Translator: Alpha, 2023\n"
"Language-Team: Korean (Korea) (https://app.transifex.com/alliance-auth/teams/107430/ko_KR/)\n" "Language-Team: Korean (Korea) (https://app.transifex.com/alliance-auth/teams/107430/ko_KR/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -35,7 +35,7 @@ msgstr "Google 애널리틱스 유니버설"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google 애널리틱스 V4" msgstr "Google 애널리틱스 V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "해당 기능을 수행하려면 주 캐릭터가 요구됩니다. 아래에서 하나를 추가하시오." msgstr "해당 기능을 수행하려면 주 캐릭터가 요구됩니다. 아래에서 하나를 추가하시오."
@ -48,63 +48,68 @@ msgstr "이메일"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "영어" msgstr "영어"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "독일어" msgstr "독일어"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "스페인어" msgstr "스페인어"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "간체자" msgstr "간체자"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "러시아어" msgstr "러시아어"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "한국어" msgstr "한국어"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "프랑스어" msgstr "프랑스어"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "일본어" msgstr "일본어"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "이탈리아어" msgstr "이탈리아어"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Language" msgid "Ukrainian"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:96
msgid "Language"
msgstr ""
#: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "야간 모드" msgstr "야간 모드"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "상태가 %s로 변경됐습니다." msgstr "상태가 %s로 변경됐습니다."
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "사용자의 상태는 %(state)s입니다." msgstr "사용자의 상태는 %(state)s입니다."
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "대시보드" msgstr "대시보드"
@ -163,8 +168,50 @@ msgstr "코퍼레이션"
msgid "Alliance" msgid "Alliance"
msgstr "얼라이언스" msgstr "얼라이언스"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "활동"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "캐릭터"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "로그인" msgstr "로그인"
@ -196,47 +243,49 @@ msgstr "등록"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "유효하지 않거나 만료된 활성화 주소" msgstr "유효하지 않거나 만료된 활성화 주소"
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
"account." "account."
msgstr "%(char)s를 주 캐릭터로 변경할 수 없음: 다른 계정이 해당 캐릭터를 소유하고 있습니다." msgstr "%(char)s를 주 캐릭터로 변경할 수 없음: 다른 계정이 해당 캐릭터를 소유하고 있습니다."
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "주 캐릭터가 %(char)s로 변경됨" msgstr "주 캐릭터가 %(char)s로 변경됨"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "계정에 %(name)s를 추가했습니다." msgstr "계정에 %(name)s를 추가했습니다."
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "계정에 %(name)s를 추가하지 못했습니다. 이미 다른 계정에 추가되었습니다." msgstr "계정에 %(name)s를 추가하지 못했습니다. 이미 다른 계정에 추가되었습니다."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "선택한 캐릭터로 인증할 수 없습니다." "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "가입 토큰이 만료되었습니다." msgstr "가입 토큰이 만료되었습니다."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
msgstr "확인 메일 전송됨. 다음 링크를 눌러 이메일 주소를 확인하세요." msgstr "확인 메일 전송됨. 다음 링크를 눌러 이메일 주소를 확인하세요."
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "이메일 주소가 확인되었습니다. 로그인 해주세요." msgstr "이메일 주소가 확인되었습니다. 로그인 해주세요."
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "현재 새로운 계정 등록은 받지않습니다." msgstr "현재 새로운 계정 등록은 받지않습니다."
@ -279,19 +328,6 @@ msgstr "미등록"
msgid "Last update:" msgid "Last update:"
msgstr "마지막 업데이트:" msgstr "마지막 업데이트:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "캐릭터"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -621,19 +657,24 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "그룹 관리" msgstr "그룹 관리"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "사용자"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "이 이름은 이미 사용되었으며 그룹의 이름으로 사용될 수 없습니다." msgstr "이 이름은 이미 사용되었으며 그룹의 이름으로 사용될 수 없습니다."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(자동)" msgstr "(자동)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "이 이름을 가진 그룹이 이미 있습니다." msgstr "이 이름을 가진 그룹이 이미 있습니다."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -642,11 +683,11 @@ msgstr ""
"시스템 그룹, 유저들은 이 그룹을 보거나, 참여하거나, 지원할 수 없습니다. <br>멤버, 코퍼레이션_*, 얼라이언스_* 등에 " "시스템 그룹, 유저들은 이 그룹을 보거나, 참여하거나, 지원할 수 없습니다. <br>멤버, 코퍼레이션_*, 얼라이언스_* 등에 "
"사용됨.<br><b>선택된 경우 비공개와 공개 옵션을 무시함.</b>" "사용됨.<br><b>선택된 경우 비공개와 공개 옵션을 무시함.</b>"
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "비공개 그룹이지만 링크를 통해 참여할 수 있음." msgstr "비공개 그룹이지만 링크를 통해 참여할 수 있음."
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -654,7 +695,7 @@ msgstr ""
"그룹은 공개되어 있으며 요청 시 유저는 자동적으로 추가됩니다.<br>그룹이 공개되어 있지 않은 경우, 유저는 직접 요청을 승인받아야 " "그룹은 공개되어 있으며 요청 시 유저는 자동적으로 추가됩니다.<br>그룹이 공개되어 있지 않은 경우, 유저는 직접 요청을 승인받아야 "
"합니다." "합니다."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -664,13 +705,13 @@ msgstr ""
"공개 그룹입니다. 등록된 모든 유저는 이 그룹에 참여할 수 있으며, 이 그룹의 설정에 따라 공개 여부가 달라집니다.<br>유저가 더 " "공개 그룹입니다. 등록된 모든 유저는 이 그룹에 참여할 수 있으며, 이 그룹의 설정에 따라 공개 여부가 달라집니다.<br>유저가 더 "
"이상 인증을 하지 않을 때, Auth는 이 그룹에서 유저를 자동 추방하지 않습니다." "이상 인증을 하지 않을 때, Auth는 이 그룹에서 유저를 자동 추방하지 않습니다."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -679,7 +720,7 @@ msgstr ""
"그룹 리더는 이 그룹의 요청을 처리할 수 있습니다. <code>auth.group_management</code> 권한을 사용하여 " "그룹 리더는 이 그룹의 요청을 처리할 수 있습니다. <code>auth.group_management</code> 권한을 사용하여 "
"사용자가 모든 그룹을 관리할 수 있도록 합니다.<br>" "사용자가 모든 그룹을 관리할 수 있도록 합니다.<br>"
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -688,46 +729,46 @@ msgstr ""
"리더 그룹의 구성원은 이 그룹에 대한 요청을 처리할 수 있습니다. <code>1auth.group_management1</code> " "리더 그룹의 구성원은 이 그룹에 대한 요청을 처리할 수 있습니다. <code>1auth.group_management1</code> "
"권한을 사용하여 사용자가 모든 그룹을 관리할 수 있도록 합니다.<br>" "권한을 사용하여 사용자가 모든 그룹을 관리할 수 있도록 합니다.<br>"
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
msgstr "만약 그들이 적절한 권한을 가졌다면, 여기 목록에 있는 신분 상태는 이 그룹에 가입할 수 있습니다." msgstr "만약 그들이 적절한 권한을 가졌다면, 여기 목록에 있는 신분 상태는 이 그룹에 가입할 수 있습니다."
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "사용자에게 나타나는 그룹에 대한 간단한 설명 <i>(최대 512자)</i> 입니다." msgstr "사용자에게 나타나는 그룹에 대한 간단한 설명 <i>(최대 512자)</i> 입니다."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "공용 그룹에 가입할 수 없음" msgstr "공용 그룹에 가입할 수 없음"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "이름" msgstr "이름"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "그룹에 사용할 수 없는 이름입니다." msgstr "그룹에 사용할 수 없는 이름입니다."
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "원인" msgstr "원인"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "이 이름이 예약된 이유입니다." msgstr "이 이름이 예약된 이유입니다."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "생성자:" msgstr "생성자:"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "생성일:" msgstr "생성일:"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "이 항목이 생성된 날짜" msgstr "이 항목이 생성된 날짜"
@ -954,86 +995,86 @@ msgstr "그룹 요청"
msgid "Group Membership" msgid "Group Membership"
msgstr "참가 중인 그룹" msgstr "참가 중인 그룹"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "유저 %(user)s이(가) %(group)s에서 제거됨." msgstr "유저 %(user)s이(가) %(group)s에서 제거됨."
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "사용자가 해당 그룹에 존재하지 않음." msgstr "사용자가 해당 그룹에 존재하지 않음."
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "그룹이 존재하지 않음." msgstr "그룹이 존재하지 않음."
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)s의 %(group)s 그룹 신청 수락" msgstr "%(mainchar)s의 %(group)s 그룹 신청 수락"
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
"%(mainchar)s to %(group)s." "%(mainchar)s to %(group)s."
msgstr "%(mainchar)s의 %(group)s 그룹 신청을 처리하는 중 알 수 없는 에러 발생" msgstr "%(mainchar)s의 %(group)s 그룹 신청을 처리하는 중 알 수 없는 에러 발생"
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)s의 %(group)s 그룹 신청 거절" msgstr "%(mainchar)s의 %(group)s 그룹 신청 거절"
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴 수락" msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴 수락"
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
"%(mainchar)s to leave %(group)s." "%(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴를 처리하는 중 알 수 없는 에러 발생" msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴를 처리하는 중 알 수 없는 에러 발생"
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴 거절" msgstr "%(mainchar)s의 %(group)s 그룹 탈퇴 거절"
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "해당 그룹에 참여할 수 없습니다." msgstr "해당 그룹에 참여할 수 없습니다."
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "이미 해당 그룹에 가입되어 있습니다." msgstr "이미 해당 그룹에 가입되어 있습니다."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "해당 그룹에 대한 참여신청이 보류되었습니다." msgstr "해당 그룹에 대한 참여신청이 보류되었습니다."
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "%(group)s그룹에 지원하였음." msgstr "%(group)s그룹에 지원하였음."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "해당 그룹을 떠날 수 없습니다." msgstr "해당 그룹을 떠날 수 없습니다."
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "해당그룹의 멤버가 아닙니다." msgstr "해당그룹의 멤버가 아닙니다."
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "해당 그룹의 탈퇴 신청이 접수된 상태입니다." msgstr "해당 그룹의 탈퇴 신청이 접수된 상태입니다."
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "%(group)s그룹의 탈퇴가 신청됨." msgstr "%(group)s그룹의 탈퇴가 신청됨."
@ -1095,16 +1136,6 @@ msgstr "지원서 작성"
msgid "Username" msgid "Username"
msgstr "사용자명" msgstr "사용자명"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "활동"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1443,10 +1474,6 @@ msgstr "모델"
msgid "Code Name" msgid "Code Name"
msgstr "코드명" msgstr "코드명"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "사용자"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "상태" msgstr "상태"
@ -2170,11 +2197,11 @@ msgid ""
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
@ -2190,11 +2217,11 @@ msgid "AA Support Discord"
msgstr "AA Support Discord" msgstr "AA Support Discord"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "사용자 매뉴" msgstr "사용자 매뉴"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "로그아웃" msgstr "로그아웃"
@ -2250,22 +2277,30 @@ msgid "Objective"
msgstr "목표 대상" msgstr "목표 대상"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "남은 일수" msgstr "남은 일수"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "남은 시간" msgstr "남은 시간"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "남은 분" msgstr "남은 분"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "중요" msgstr "중요"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "코퍼레이션 제한" msgstr "코퍼레이션 제한"

View File

@ -4,8 +4,8 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Yuriy K <thedjcooltv@gmail.com>, 2023
# Андрей Зубков <and.vareba81@gmail.com>, 2023 # Андрей Зубков <and.vareba81@gmail.com>, 2023
# Yuriy K <thedjcooltv@gmail.com>, 2023
# Alexander Gess <de.alex.gess@gmail.com>, 2023 # Alexander Gess <de.alex.gess@gmail.com>, 2023
# Filipp Chertiev <f@fzfx.ru>, 2023 # Filipp Chertiev <f@fzfx.ru>, 2023
# #
@ -14,8 +14,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Filipp Chertiev <f@fzfx.ru>, 2023\n" "Last-Translator: Filipp Chertiev <f@fzfx.ru>, 2023\n"
"Language-Team: Russian (https://app.transifex.com/alliance-auth/teams/107430/ru/)\n" "Language-Team: Russian (https://app.transifex.com/alliance-auth/teams/107430/ru/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -32,7 +32,7 @@ msgstr "Google Analytics Universal"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google Analytics V4" msgstr "Google Analytics V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
"Для продолжения следует указать основного персонажа. Выберите его ниже." "Для продолжения следует указать основного персонажа. Выберите его ниже."
@ -46,63 +46,68 @@ msgstr "Email"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "Вам не разрешено добавлять или удалять эти ограниченные группы: %s" msgstr "Вам не разрешено добавлять или удалять эти ограниченные группы: %s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "Английский" msgstr "Английский"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "Немецкий" msgstr "Немецкий"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "Испанский" msgstr "Испанский"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Китайский упрощённый" msgstr "Китайский упрощённый"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "Русский" msgstr "Русский"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "Корейский" msgstr "Корейский"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "Французский" msgstr "Французский"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "Японский" msgstr "Японский"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "Итальянский" msgstr "Итальянский"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "Язык" msgstr "Язык"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "Ночной режим" msgstr "Ночной режим"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "Статус изменен: %s" msgstr "Статус изменен: %s"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "Статус пилота: %(state)s" msgstr "Статус пилота: %(state)s"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "Панель показателей" msgstr "Панель показателей"
@ -161,8 +166,50 @@ msgstr "Корпорация"
msgid "Alliance" msgid "Alliance"
msgstr "Альянс" msgstr "Альянс"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Действия"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Персонаж"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "Вход" msgstr "Вход"
@ -195,7 +242,7 @@ msgstr "Регистрация"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "Ссылка активации устарела" msgstr "Ссылка активации устарела"
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
@ -203,40 +250,42 @@ msgid ""
msgstr "" msgstr ""
"Нельзя сменить основного персонажа на %(char)s: похоже, что Владелец не Вы. " "Нельзя сменить основного персонажа на %(char)s: похоже, что Владелец не Вы. "
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "Основной персонаж заменен на %(char)s" msgstr "Основной персонаж заменен на %(char)s"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "Добавлен %(name)s на Ваш аккаунт." msgstr "Добавлен %(name)s на Ваш аккаунт."
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "Персонаж %(name)s уже добавлен." msgstr "Персонаж %(name)s уже добавлен."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "Невозможно авторизировать этого персонажа. " "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "Регистрационный токен просрочен." msgstr "Регистрационный токен просрочен."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
msgstr "Отправить подтверждающее письмо. Пожалуйста, подтвердите почту. " msgstr "Отправить подтверждающее письмо. Пожалуйста, подтвердите почту. "
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "Подтвердите Ваш email адрес. Зайти для подтверждения. " msgstr "Подтвердите Ваш email адрес. Зайти для подтверждения. "
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "Регистрация новых аккаунтов в настоящее время невозможна." msgstr "Регистрация новых аккаунтов в настоящее время невозможна."
@ -279,19 +328,6 @@ msgstr "Не зарегистрированы"
msgid "Last update:" msgid "Last update:"
msgstr "Последнее обновление: " msgstr "Последнее обновление: "
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Персонаж"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -629,20 +665,25 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "Управление Группой" msgstr "Управление Группой"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Пользователи"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "" msgstr ""
"Это имя является зарезервированным и не может быть использовано для групп." "Это имя является зарезервированным и не может быть использовано для групп."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(авто)" msgstr "(авто)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "Группа с таким именем уже существует." msgstr "Группа с таким именем уже существует."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -653,13 +694,13 @@ msgstr ""
"Members, Corp_*, Alliance_* и т. п.<br><b>Будучи выбранной, отменяет " "Members, Corp_*, Alliance_* и т. п.<br><b>Будучи выбранной, отменяет "
"настройки \"Скрытая\" и \"Открытая\".</b>" "настройки \"Скрытая\" и \"Открытая\".</b>"
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
"Группы скрыты от пользователей, но к ним всё ещё можно присоединиться с " "Группы скрыты от пользователей, но к ним всё ещё можно присоединиться с "
"помощью корректной ссылки." "помощью корректной ссылки."
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -668,7 +709,7 @@ msgstr ""
"при отправке запроса.<br>Если группа не является открытой, запросы от " "при отправке запроса.<br>Если группа не является открытой, запросы от "
"пользователей будут требовать ручного подтверждения." "пользователей будут требовать ручного подтверждения."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -680,7 +721,7 @@ msgstr ""
"настройках данной группы.<br>Auth не будет удалять пользователей из этой " "настройках данной группы.<br>Auth не будет удалять пользователей из этой "
"группы автоматически при окончании срока их аутентификации." "группы автоматически при окончании срока их аутентификации."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
@ -688,7 +729,7 @@ msgstr ""
"Группа является ограниченной. Это значит что добавление пользователей в эту " "Группа является ограниченной. Это значит что добавление пользователей в эту "
"группу или удаление из неё требует прав superuser admin." "группу или удаление из неё требует прав superuser admin."
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -698,7 +739,7 @@ msgstr ""
"Используйте разрешение <code>auth.group_management</code>, чтобы позволить " "Используйте разрешение <code>auth.group_management</code>, чтобы позволить "
"пользователю управлять всеми группами.<br>" "пользователю управлять всеми группами.<br>"
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -708,7 +749,7 @@ msgstr ""
"Используйте разрешение <code>auth.group_management</code>, чтобы позволить " "Используйте разрешение <code>auth.group_management</code>, чтобы позволить "
"пользователю управлять всеми группами.<br>" "пользователю управлять всеми группами.<br>"
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
@ -716,42 +757,42 @@ msgstr ""
"Статусы, перечисленные здесь, смогут присоединиться к группе, если у них " "Статусы, перечисленные здесь, смогут присоединиться к группе, если у них "
"есть соответствующие разрешения.<br>" "есть соответствующие разрешения.<br>"
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
"Краткое описание <i>(макс. 512 символов)</i> группы, отображаемое " "Краткое описание <i>(макс. 512 символов)</i> группы, отображаемое "
"пользователям." "пользователям."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "Можно отправлять запрос на непубличную группу." msgstr "Можно отправлять запрос на непубличную группу."
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "имя" msgstr "имя"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "Имя, которое не может быть использовано для групп." msgstr "Имя, которое не может быть использовано для групп."
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "причина" msgstr "причина"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "Причина, по которой это имя зарезервировано." msgstr "Причина, по которой это имя зарезервировано."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "создано кем" msgstr "создано кем"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "создано когда" msgstr "создано когда"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "Дата, когда данное содержимое было создано" msgstr "Дата, когда данное содержимое было создано"
@ -978,26 +1019,26 @@ msgstr "Групповой запрос"
msgid "Group Membership" msgid "Group Membership"
msgstr "Групповое участие" msgstr "Групповое участие"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "Пользователь %(user)s исключен из %(group)s." msgstr "Пользователь %(user)s исключен из %(group)s."
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "Пользователь не существует в этой группе." msgstr "Пользователь не существует в этой группе."
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "Группа не существует." msgstr "Группа не существует."
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "Запрос от %(mainchar)sв %(group)s принят." msgstr "Запрос от %(mainchar)sв %(group)s принят."
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1006,18 +1047,18 @@ msgstr ""
"Персонаж %(mainchar)s не может быть добавлен %(group)s, из-за непредвиденной" "Персонаж %(mainchar)s не может быть добавлен %(group)s, из-за непредвиденной"
" ошибки. " " ошибки. "
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)s исключен из %(group)s." msgstr "%(mainchar)s исключен из %(group)s."
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "Утвержден выход %(mainchar)s из %(group)s. " msgstr "Утвержден выход %(mainchar)s из %(group)s. "
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1026,42 +1067,42 @@ msgstr ""
"Возникла ошибка во время обработки %(mainchar)s на выход из группы " "Возникла ошибка во время обработки %(mainchar)s на выход из группы "
"%(group)s. Повторите позже." "%(group)s. Повторите позже."
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "Прошение об исключении %(mainchar)s из %(group)s отклонено. " msgstr "Прошение об исключении %(mainchar)s из %(group)s отклонено. "
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "Вы не можете вступить" msgstr "Вы не можете вступить"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "Вы уже участник этой группы." msgstr "Вы уже участник этой группы."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "Вы уже подали заявку на вступление этой группы." msgstr "Вы уже подали заявку на вступление этой группы."
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "Вступить в группу %(group)s." msgstr "Вступить в группу %(group)s."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "Вы не можете покинуть эту группу" msgstr "Вы не можете покинуть эту группу"
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "Вы не участник группыы" msgstr "Вы не участник группыы"
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "Ваш запрос находится на рассмотрении" msgstr "Ваш запрос находится на рассмотрении"
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "Запрос на выход из группы %(group)s." msgstr "Запрос на выход из группы %(group)s."
@ -1123,16 +1164,6 @@ msgstr "Сделать запрос"
msgid "Username" msgid "Username"
msgstr "Пользователь" msgstr "Пользователь"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Действия"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1471,10 +1502,6 @@ msgstr "Модель"
msgid "Code Name" msgid "Code Name"
msgstr "Кодовое имя" msgstr "Кодовое имя"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Пользователи"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "Статусы" msgstr "Статусы"
@ -2216,14 +2243,12 @@ msgstr ""
" Статус %(total)s обработанных задач • последние %(latest)s" " Статус %(total)s обработанных задач • последние %(latest)s"
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
"\n"
" %(queue_length)s запланированных задач"
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2238,11 +2263,11 @@ msgid "AA Support Discord"
msgstr "Discord поддержки AA" msgstr "Discord поддержки AA"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "Меню пользователя" msgstr "Меню пользователя"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "Выход" msgstr "Выход"
@ -2298,22 +2323,30 @@ msgid "Objective"
msgstr "Задача" msgstr "Задача"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "Дней осталось" msgstr "Дней осталось"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "Часов осталось" msgstr "Часов осталось"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "Минут осталось" msgstr "Минут осталось"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "Важно" msgstr "Важно"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "Корпорация зарегистрированна" msgstr "Корпорация зарегистрированна"

View File

@ -12,8 +12,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Kristof Swensen, 2023\n" "Last-Translator: Kristof Swensen, 2023\n"
"Language-Team: Ukrainian (https://app.transifex.com/alliance-auth/teams/107430/uk/)\n" "Language-Team: Ukrainian (https://app.transifex.com/alliance-auth/teams/107430/uk/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -30,7 +30,7 @@ msgstr "Універсальна Google Аналітика"
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "Google Analytics V4" msgstr "Google Analytics V4"
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "" msgstr ""
"Для виконання цієї дії потрібен основний персонаж. Додайте його нижче." "Для виконання цієї дії потрібен основний персонаж. Додайте його нижче."
@ -44,63 +44,68 @@ msgstr "Електронна пошта"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "Вам заборонено додавати або видаляти ці обмежені групи: %s" msgstr "Вам заборонено додавати або видаляти ці обмежені групи: %s"
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "Англійська" msgstr "Англійська"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "Німецька" msgstr "Німецька"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "Іспанська" msgstr "Іспанська"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Китайська спрощена" msgstr "Китайська спрощена"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "Російська" msgstr "Російська"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "Корейська" msgstr "Корейська"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "Французька" msgstr "Французька"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "Японська" msgstr "Японська"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "Італійська" msgstr "Італійська"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "Мова" msgstr "Мова"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "Нічний режим" msgstr "Нічний режим"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "Стан змінено на: %s" msgstr "Стан змінено на: %s"
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "Стан вашого користувача зараз: %(state)s" msgstr "Стан вашого користувача зараз: %(state)s"
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "Панель приладів" msgstr "Панель приладів"
@ -158,8 +163,50 @@ msgstr "Корпорація"
msgid "Alliance" msgid "Alliance"
msgstr "Альянс" msgstr "Альянс"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Дії"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Персонаж"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "Увійти" msgstr "Увійти"
@ -193,7 +240,7 @@ msgstr "Зареєструватися"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "Невірне або прострочене посилання для активації." msgstr "Невірне або прострочене посилання для активації."
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
@ -202,32 +249,34 @@ msgstr ""
"Неможливо змінити основного персонажа на %(char)s: персонаж належить іншому " "Неможливо змінити основного персонажа на %(char)s: персонаж належить іншому "
"акаунту." "акаунту."
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "Основний персонаж змінено на %(char)s" msgstr "Основний персонаж змінено на %(char)s"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "Додано %(name)s до вашого облікового запису." msgstr "Додано %(name)s до вашого облікового запису."
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "" msgstr ""
"Не вдалося додати %(name)s до вашого облікового запису: у них вже є " "Не вдалося додати %(name)s до вашого облікового запису: у них вже є "
"обліковий запис." "обліковий запис."
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "Не вдалося автентифікуватися як обраний персонаж." "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "Токен реєстрації застарів." msgstr "Токен реєстрації застарів."
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
@ -235,13 +284,13 @@ msgstr ""
"Відправлено лист з підтвердженням. Будь ласка, перейдіть за посиланням, щоб " "Відправлено лист з підтвердженням. Будь ласка, перейдіть за посиланням, щоб "
"підтвердити свою адресу електронної пошти." "підтвердити свою адресу електронної пошти."
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "" msgstr ""
"Підтверджено вашу адресу електронної пошти. Будь ласка, увійдіть, щоб " "Підтверджено вашу адресу електронної пошти. Будь ласка, увійдіть, щоб "
"продовжити." "продовжити."
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "Реєстрація нових облікових записів наразі не дозволена." msgstr "Реєстрація нових облікових записів наразі не дозволена."
@ -284,19 +333,6 @@ msgstr "Незареєстровані"
msgid "Last update:" msgid "Last update:"
msgstr "Останнє оновлення:" msgstr "Останнє оновлення:"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "Персонаж"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -634,19 +670,24 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "Керування групами" msgstr "Керування групами"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Користувачі"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "Це ім'я зарезервоване і не може бути використане для груп." msgstr "Це ім'я зарезервоване і не може бути використане для груп."
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "(авто)" msgstr "(авто)"
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "Група з таким ім'ям вже існує." msgstr "Група з таким ім'ям вже існує."
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
@ -657,12 +698,12 @@ msgstr ""
"\"Members, Corp_, Alliance_ і т.д.<br><b>Перевизначає параметри Hidden і \"\n" "\"Members, Corp_, Alliance_ і т.д.<br><b>Перевизначає параметри Hidden і \"\n"
"\"Open при виборі.</b>\"" "\"Open при виборі.</b>\""
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
"Група прихована від користувачів, але можна приєднатися за посиланням." "Група прихована від користувачів, але можна приєднатися за посиланням."
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
@ -671,7 +712,7 @@ msgstr ""
"запитом.<br>Якщо група закрита, користувачі повинні отримати ручне " "запитом.<br>Якщо група закрита, користувачі повинні отримати ручне "
"підтвердження запиту." "підтвердження запиту."
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -683,7 +724,7 @@ msgstr ""
"групи.<br>Авторизація не буде автоматично видаляти користувачів з цієї " "групи.<br>Авторизація не буде автоматично видаляти користувачів з цієї "
"групи, коли вони більше не автентифіковані." "групи, коли вони більше не автентифіковані."
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
@ -691,7 +732,7 @@ msgstr ""
"Група обмежена. Це означає, що додавання або видалення користувачів для цієї" "Група обмежена. Це означає, що додавання або видалення користувачів для цієї"
" групи вимагає адміністратора-суперкористувача." " групи вимагає адміністратора-суперкористувача."
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -701,7 +742,7 @@ msgstr ""
"<code>auth.group_management</code>, щоб дозволити користувачеві керувати " "<code>auth.group_management</code>, щоб дозволити користувачеві керувати "
"всіма групами.<br>" "всіма групами.<br>"
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
@ -711,7 +752,7 @@ msgstr ""
" дозвіл <code>auth.group_management</code>, щоб дозволити користувачеві " " дозвіл <code>auth.group_management</code>, щоб дозволити користувачеві "
"керувати всіма групами.<br>" "керувати всіма групами.<br>"
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
@ -719,42 +760,42 @@ msgstr ""
"Штати, перераховані тут, матимуть змогу приєднатися до цієї групи, якщо вони" "Штати, перераховані тут, матимуть змогу приєднатися до цієї групи, якщо вони"
" мають відповідні дозволи.<br>" " мають відповідні дозволи.<br>"
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
"Короткий опис <i>(максимум 512 символів)</i> групи, що відображається " "Короткий опис <i>(максимум 512 символів)</i> групи, що відображається "
"користувачам." "користувачам."
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "Може запитувати непублічні групи" msgstr "Може запитувати непублічні групи"
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "назва" msgstr "назва"
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "Назва, яку неможна використовувати для груп." msgstr "Назва, яку неможна використовувати для груп."
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "причина" msgstr "причина"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "Причина, чому ця назва зарезервована." msgstr "Причина, чому ця назва зарезервована."
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "створено користувачем" msgstr "створено користувачем"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "створено о" msgstr "створено о"
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "Дата створення цього запису" msgstr "Дата створення цього запису"
@ -981,26 +1022,26 @@ msgstr "Групові запити"
msgid "Group Membership" msgid "Group Membership"
msgstr "Членство в групі" msgstr "Членство в групі"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "Користувач %(user)s вилучений з групи %(group)s." msgstr "Користувач %(user)s вилучений з групи %(group)s."
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "Користувача не існує в цій групі" msgstr "Користувача не існує в цій групі"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "Група не існує" msgstr "Група не існує"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "Заявка %(mainchar)s на вступ до %(group)s прийнята." msgstr "Заявка %(mainchar)s на вступ до %(group)s прийнята."
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1009,18 +1050,18 @@ msgstr ""
"Під час обробки заявки %(mainchar)s на вступ до %(group)s виникла помилка, " "Під час обробки заявки %(mainchar)s на вступ до %(group)s виникла помилка, "
"яку не можна обробити." "яку не можна обробити."
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "Заявка %(mainchar)s на вступ до %(group)s відхилена." msgstr "Заявка %(mainchar)s на вступ до %(group)s відхилена."
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "Заявка %(mainchar)s на вихід з %(group)s прийнята." msgstr "Заявка %(mainchar)s на вихід з %(group)s прийнята."
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
@ -1029,42 +1070,42 @@ msgstr ""
"Під час обробки заявки %(mainchar)s на вихід з %(group)s виникла помилка, " "Під час обробки заявки %(mainchar)s на вихід з %(group)s виникла помилка, "
"яку не можна обробити." "яку не можна обробити."
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "Заявка %(mainchar)s на вихід з %(group)s відхилена." msgstr "Заявка %(mainchar)s на вихід з %(group)s відхилена."
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "Ви не можете приєднатись до цієї групи" msgstr "Ви не можете приєднатись до цієї групи"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "Ви вже є членом цієї групи." msgstr "Ви вже є членом цієї групи."
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "Ви вже подали заявку на вступ до цієї групи." msgstr "Ви вже подали заявку на вступ до цієї групи."
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "Подано заявку на групу %(group)s." msgstr "Подано заявку на групу %(group)s."
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "Ви не можете покинути цю групу" msgstr "Ви не можете покинути цю групу"
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "Ви не є учасником цієї групи" msgstr "Ви не є учасником цієї групи"
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "Ви вже подали запит на вихід з цієї групи." msgstr "Ви вже подали запит на вихід з цієї групи."
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "Подано заявку на вихід з групи %(group)s." msgstr "Подано заявку на вихід з групи %(group)s."
@ -1126,16 +1167,6 @@ msgstr "Створити заявку"
msgid "Username" msgid "Username"
msgstr "Ім'я користувача" msgstr "Ім'я користувача"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "Дії"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1474,10 +1505,6 @@ msgstr "Модель"
msgid "Code Name" msgid "Code Name"
msgstr "Кодова назва" msgstr "Кодова назва"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "Користувачі"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "Стани" msgstr "Стани"
@ -2220,14 +2247,12 @@ msgstr ""
"Статус %(total)s виконаних завдань • останній %(latest)s" "Статус %(total)s виконаних завдань • останній %(latest)s"
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
"\n"
"%(queue_length)s завдань в черзі"
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin" msgid "Admin"
@ -2242,11 +2267,11 @@ msgid "AA Support Discord"
msgstr "Підтримка AA у Discord" msgstr "Підтримка AA у Discord"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "Меню Користувача" msgstr "Меню Користувача"
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "Вихід" msgstr "Вихід"
@ -2302,22 +2327,30 @@ msgid "Objective"
msgstr "Мета" msgstr "Мета"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "Залишилося днів" msgstr "Залишилося днів"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "Залишилося годин" msgstr "Залишилося годин"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "Залишилося хвилин" msgstr "Залишилося хвилин"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "Важливо" msgstr "Важливо"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "Обмежено для корпорації" msgstr "Обмежено для корпорації"

View File

@ -4,19 +4,19 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Shen Yang, 2023
# Jesse . <sgeine@hotmail.com>, 2023 # Jesse . <sgeine@hotmail.com>, 2023
# Aaron BuBu <351793078@qq.com>, 2023 # Aaron BuBu <351793078@qq.com>, 2023
# Joel Falknau <ozirascal@gmail.com>, 2023 # Joel Falknau <ozirascal@gmail.com>, 2023
# Shen Yang, 2023
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-09 18:20+1000\n" "POT-Creation-Date: 2024-02-17 18:50+1000\n"
"PO-Revision-Date: 2023-10-08 09:23+0000\n" "PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Shen Yang, 2023\n" "Last-Translator: Joel Falknau <ozirascal@gmail.com>, 2023\n"
"Language-Team: Chinese Simplified (https://app.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n" "Language-Team: Chinese Simplified (https://app.transifex.com/alliance-auth/teams/107430/zh-Hans/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -32,7 +32,7 @@ msgstr ""
msgid "Google Analytics V4" msgid "Google Analytics V4"
msgstr "" msgstr ""
#: allianceauth/authentication/decorators.py:37 #: allianceauth/authentication/decorators.py:49
msgid "A main character is required to perform that action. Add one below." msgid "A main character is required to perform that action. Add one below."
msgstr "只有主要角色才能执行这个操作。在下面添加一个" msgstr "只有主要角色才能执行这个操作。在下面添加一个"
@ -45,63 +45,68 @@ msgstr "电子邮箱"
msgid "You are not allowed to add or remove these restricted groups: %s" msgid "You are not allowed to add or remove these restricted groups: %s"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:80 #: allianceauth/authentication/models.py:71
msgid "English" msgid "English"
msgstr "英语" msgstr "英语"
#: allianceauth/authentication/models.py:81 #: allianceauth/authentication/models.py:72
msgid "German" msgid "German"
msgstr "德语" msgstr "德语"
#: allianceauth/authentication/models.py:82 #: allianceauth/authentication/models.py:73
msgid "Spanish" msgid "Spanish"
msgstr "西班牙语" msgstr "西班牙语"
#: allianceauth/authentication/models.py:83 #: allianceauth/authentication/models.py:74
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "简体中文" msgstr "简体中文"
#: allianceauth/authentication/models.py:84 #: allianceauth/authentication/models.py:75
msgid "Russian" msgid "Russian"
msgstr "俄语" msgstr "俄语"
#: allianceauth/authentication/models.py:85 #: allianceauth/authentication/models.py:76
msgid "Korean" msgid "Korean"
msgstr "韩语" msgstr "韩语"
#: allianceauth/authentication/models.py:86 #: allianceauth/authentication/models.py:77
msgid "French" msgid "French"
msgstr "法语" msgstr "法语"
#: allianceauth/authentication/models.py:87 #: allianceauth/authentication/models.py:78
msgid "Japanese" msgid "Japanese"
msgstr "日语" msgstr "日语"
#: allianceauth/authentication/models.py:88 #: allianceauth/authentication/models.py:79
msgid "Italian" msgid "Italian"
msgstr "意大利语" msgstr "意大利语"
#: allianceauth/authentication/models.py:91 #: allianceauth/authentication/models.py:80
msgid "Ukrainian"
msgstr ""
#: allianceauth/authentication/models.py:96
msgid "Language" msgid "Language"
msgstr "语言" msgstr "语言"
#: allianceauth/authentication/models.py:96 #: allianceauth/authentication/models.py:101
#: allianceauth/templates/allianceauth/night-toggle.html:6 #: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode" msgid "Night Mode"
msgstr "夜间模式" msgstr "夜间模式"
#: allianceauth/authentication/models.py:110 #: allianceauth/authentication/models.py:115
#, python-format #, python-format
msgid "State changed to: %s" msgid "State changed to: %s"
msgstr "" msgstr ""
#: allianceauth/authentication/models.py:111 #: allianceauth/authentication/models.py:116
#, python-format #, python-format
msgid "Your user's state is now: %(state)s" msgid "Your user's state is now: %(state)s"
msgstr "" msgstr ""
#: allianceauth/authentication/templates/authentication/dashboard.html:4 #: allianceauth/authentication/templates/authentication/dashboard.html:4
#: allianceauth/authentication/templates/authentication/dashboard.html:7 #: allianceauth/authentication/templates/authentication/dashboard.html:7
#: allianceauth/authentication/templates/authentication/tokens.html:4
#: allianceauth/templates/allianceauth/side-menu.html:10 #: allianceauth/templates/allianceauth/side-menu.html:10
msgid "Dashboard" msgid "Dashboard"
msgstr "账户总览" msgstr "账户总览"
@ -157,8 +162,50 @@ msgstr "所在公司"
msgid "Alliance" msgid "Alliance"
msgstr "所在联盟" msgstr "所在联盟"
#: allianceauth/authentication/templates/authentication/tokens.html:7
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:62
msgid "Token Management"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:12
msgid "Scopes"
msgstr ""
#: allianceauth/authentication/templates/authentication/tokens.html:13
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "操作"
#: allianceauth/authentication/templates/authentication/tokens.html:14
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "角色"
#: allianceauth/authentication/templates/authentication/tokens.html:28
msgid ""
"This page is a best attempt, but backups or database logs can still contain "
"your tokens. Always revoke tokens on "
"https://community.eveonline.com/support/third-party-applications/ where "
"possible."
msgstr ""
#: allianceauth/authentication/templates/public/login.html:6 #: allianceauth/authentication/templates/public/login.html:6
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:58 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:69
msgid "Login" msgid "Login"
msgstr "登录" msgstr "登录"
@ -190,47 +237,49 @@ msgstr "注册"
msgid "Invalid or expired activation link." msgid "Invalid or expired activation link."
msgstr "激活链接无效或过期" msgstr "激活链接无效或过期"
#: allianceauth/authentication/views.py:77 #: allianceauth/authentication/views.py:118
#, python-format #, python-format
msgid "" msgid ""
"Cannot change main character to %(char)s: character owned by a different " "Cannot change main character to %(char)s: character owned by a different "
"account." "account."
msgstr "不能修改主角色为%(char)s这个角色被另一个账户所拥有" msgstr "不能修改主角色为%(char)s这个角色被另一个账户所拥有"
#: allianceauth/authentication/views.py:83 #: allianceauth/authentication/views.py:124
#, python-format #, python-format
msgid "Changed main character to %(char)s" msgid "Changed main character to %(char)s"
msgstr "修改主要角色为%(char)s" msgstr "修改主要角色为%(char)s"
#: allianceauth/authentication/views.py:92 #: allianceauth/authentication/views.py:133
#, python-format #, python-format
msgid "Added %(name)s to your account." msgid "Added %(name)s to your account."
msgstr "添加%(name)s到您的账户" msgstr "添加%(name)s到您的账户"
#: allianceauth/authentication/views.py:94 #: allianceauth/authentication/views.py:135
#, python-format #, python-format
msgid "Failed to add %(name)s to your account: they already have an account." msgid "Failed to add %(name)s to your account: they already have an account."
msgstr "添加%(name)s到您的账户失败他们已经在一个账户中了" msgstr "添加%(name)s到您的账户失败他们已经在一个账户中了"
#: allianceauth/authentication/views.py:133 #: allianceauth/authentication/views.py:178
msgid "Unable to authenticate as the selected character." msgid ""
msgstr "无法作为选定的角色进行身份验证" "Unable to authenticate as the selected character. Please log in with the "
"main character associated with this account."
msgstr ""
#: allianceauth/authentication/views.py:197 #: allianceauth/authentication/views.py:244
msgid "Registration token has expired." msgid "Registration token has expired."
msgstr "注册令牌过期。" msgstr "注册令牌过期。"
#: allianceauth/authentication/views.py:252 #: allianceauth/authentication/views.py:302
msgid "" msgid ""
"Sent confirmation email. Please follow the link to confirm your email " "Sent confirmation email. Please follow the link to confirm your email "
"address." "address."
msgstr "已经发送了确认邮件。请按照链接确定您的电邮地址" msgstr "已经发送了确认邮件。请按照链接确定您的电邮地址"
#: allianceauth/authentication/views.py:257 #: allianceauth/authentication/views.py:307
msgid "Confirmed your email address. Please login to continue." msgid "Confirmed your email address. Please login to continue."
msgstr "已确认您的电邮地址。请登录以继续" msgstr "已确认您的电邮地址。请登录以继续"
#: allianceauth/authentication/views.py:262 #: allianceauth/authentication/views.py:312
msgid "Registration of new accounts is not allowed at this time." msgid "Registration of new accounts is not allowed at this time."
msgstr "" msgstr ""
@ -273,19 +322,6 @@ msgstr "未注册成员"
msgid "Last update:" msgid "Last update:"
msgstr "最后一次更新" msgstr "最后一次更新"
#: allianceauth/corputils/templates/corputils/corpstats.html:74
#: allianceauth/corputils/templates/corputils/corpstats.html:112
#: allianceauth/corputils/templates/corputils/corpstats.html:156
#: allianceauth/corputils/templates/corputils/search.html:13
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:26
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:30
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembers.html:29
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:55
#: allianceauth/groupmanagement/templates/groupmanagement/index.html:112
msgid "Character"
msgstr "角色"
#: allianceauth/corputils/templates/corputils/corpstats.html:75 #: allianceauth/corputils/templates/corputils/corpstats.html:75
#: allianceauth/corputils/templates/corputils/search.html:14 #: allianceauth/corputils/templates/corputils/search.html:14
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31 #: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
@ -615,36 +651,41 @@ msgstr ""
msgid "Group Management" msgid "Group Management"
msgstr "用户组管理" msgstr "用户组管理"
#: allianceauth/groupmanagement/forms.py:15 #: allianceauth/groupmanagement/forms.py:18
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "用户"
#: allianceauth/groupmanagement/forms.py:52
msgid "This name has been reserved and can not be used for groups." msgid "This name has been reserved and can not be used for groups."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/forms.py:25 #: allianceauth/groupmanagement/forms.py:62
msgid "(auto)" msgid "(auto)"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/forms.py:34 #: allianceauth/groupmanagement/forms.py:71
msgid "There already exists a group with that name." msgid "There already exists a group with that name."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:105 #: allianceauth/groupmanagement/models.py:104
msgid "" msgid ""
"Internal group, users cannot see, join or request to join this " "Internal group, users cannot see, join or request to join this "
"group.<br>Used for groups such as Members, Corp_*, Alliance_* " "group.<br>Used for groups such as Members, Corp_*, Alliance_* "
"etc.<br><b>Overrides Hidden and Open options when selected.</b>" "etc.<br><b>Overrides Hidden and Open options when selected.</b>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:113 #: allianceauth/groupmanagement/models.py:112
msgid "Group is hidden from users but can still join with the correct link." msgid "Group is hidden from users but can still join with the correct link."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:119 #: allianceauth/groupmanagement/models.py:118
msgid "" msgid ""
"Group is open and users will be automatically added upon request.<br>If the " "Group is open and users will be automatically added upon request.<br>If the "
"group is not open users will need their request manually approved." "group is not open users will need their request manually approved."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:126 #: allianceauth/groupmanagement/models.py:125
msgid "" msgid ""
"Group is public. Any registered user is able to join this group, with " "Group is public. Any registered user is able to join this group, with "
"visibility based on the other options set for this group.<br>Auth will not " "visibility based on the other options set for this group.<br>Auth will not "
@ -652,66 +693,66 @@ msgid ""
"authenticated." "authenticated."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:135 #: allianceauth/groupmanagement/models.py:134
msgid "" msgid ""
"Group is restricted. This means that adding or removing users for this group" "Group is restricted. This means that adding or removing users for this group"
" requires a superuser admin." " requires a superuser admin."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:144 #: allianceauth/groupmanagement/models.py:143
msgid "" msgid ""
"Group leaders can process requests for this group. Use the " "Group leaders can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
"groups.<br>" "groups.<br>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:154 #: allianceauth/groupmanagement/models.py:153
msgid "" msgid ""
"Members of leader groups can process requests for this group. Use the " "Members of leader groups can process requests for this group. Use the "
"<code>auth.group_management</code> permission to allow a user to manage all " "<code>auth.group_management</code> permission to allow a user to manage all "
"groups.<br>" "groups.<br>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:163 #: allianceauth/groupmanagement/models.py:162
msgid "" msgid ""
"States listed here will have the ability to join this group provided they " "States listed here will have the ability to join this group provided they "
"have the proper permissions.<br>" "have the proper permissions.<br>"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:171 #: allianceauth/groupmanagement/models.py:170
msgid "" msgid ""
"Short description <i>(max. 512 characters)</i> of the group shown to users." "Short description <i>(max. 512 characters)</i> of the group shown to users."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:178 #: allianceauth/groupmanagement/models.py:177
msgid "Can request non-public groups" msgid "Can request non-public groups"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:209 #: allianceauth/groupmanagement/models.py:208
msgid "name" msgid "name"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:212 #: allianceauth/groupmanagement/models.py:211
msgid "Name that can not be used for groups." msgid "Name that can not be used for groups."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "reason" msgid "reason"
msgstr "原因" msgstr "原因"
#: allianceauth/groupmanagement/models.py:215 #: allianceauth/groupmanagement/models.py:214
msgid "Reason why this name is reserved." msgid "Reason why this name is reserved."
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:218 #: allianceauth/groupmanagement/models.py:217
msgid "created by" msgid "created by"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "created at" msgid "created at"
msgstr "" msgstr ""
#: allianceauth/groupmanagement/models.py:223 #: allianceauth/groupmanagement/models.py:222
msgid "Date when this entry was created" msgid "Date when this entry was created"
msgstr "" msgstr ""
@ -938,86 +979,86 @@ msgstr "用户组请求"
msgid "Group Membership" msgid "Group Membership"
msgstr "用户组成员" msgstr "用户组成员"
#: allianceauth/groupmanagement/views.py:163 #: allianceauth/groupmanagement/views.py:166
#, python-format #, python-format
msgid "Removed user %(user)s from group %(group)s." msgid "Removed user %(user)s from group %(group)s."
msgstr "已将用户%(user)s从用户组%(group)s中移除" msgstr "已将用户%(user)s从用户组%(group)s中移除"
#: allianceauth/groupmanagement/views.py:165 #: allianceauth/groupmanagement/views.py:168
msgid "User does not exist in that group" msgid "User does not exist in that group"
msgstr "那个用户组中不存在这个用户" msgstr "那个用户组中不存在这个用户"
#: allianceauth/groupmanagement/views.py:168 #: allianceauth/groupmanagement/views.py:171
msgid "Group does not exist" msgid "Group does not exist"
msgstr "用户组不存在" msgstr "用户组不存在"
#: allianceauth/groupmanagement/views.py:195 #: allianceauth/groupmanagement/views.py:198
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to %(group)s." msgid "Accepted application from %(mainchar)s to %(group)s."
msgstr "已接受用户%(mainchar)s加入%(group)s的申请" msgstr "已接受用户%(mainchar)s加入%(group)s的申请"
#: allianceauth/groupmanagement/views.py:201 #: allianceauth/groupmanagement/views.py:204
#: allianceauth/groupmanagement/views.py:232 #: allianceauth/groupmanagement/views.py:235
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
"%(mainchar)s to %(group)s." "%(mainchar)s to %(group)s."
msgstr "在处理用户%(mainchar)s加入%(group)s的申请的过程中出现了一个搞不定的错误" msgstr "在处理用户%(mainchar)s加入%(group)s的申请的过程中出现了一个搞不定的错误"
#: allianceauth/groupmanagement/views.py:226 #: allianceauth/groupmanagement/views.py:229
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to %(group)s." msgid "Rejected application from %(mainchar)s to %(group)s."
msgstr "%(mainchar)s加入%(group)s的申请已拒绝" msgstr "%(mainchar)s加入%(group)s的申请已拒绝"
#: allianceauth/groupmanagement/views.py:261 #: allianceauth/groupmanagement/views.py:264
#, python-format #, python-format
msgid "Accepted application from %(mainchar)s to leave %(group)s." msgid "Accepted application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s加入%(group)s的申请已通过" msgstr "%(mainchar)s加入%(group)s的申请已通过"
#: allianceauth/groupmanagement/views.py:266 #: allianceauth/groupmanagement/views.py:269
#: allianceauth/groupmanagement/views.py:298 #: allianceauth/groupmanagement/views.py:301
#, python-format #, python-format
msgid "" msgid ""
"An unhandled error occurred while processing the application from " "An unhandled error occurred while processing the application from "
"%(mainchar)s to leave %(group)s." "%(mainchar)s to leave %(group)s."
msgstr "在处理%(mainchar)s离开%(group)s的程序时发生了未知的错误" msgstr "在处理%(mainchar)s离开%(group)s的程序时发生了未知的错误"
#: allianceauth/groupmanagement/views.py:292 #: allianceauth/groupmanagement/views.py:295
#, python-format #, python-format
msgid "Rejected application from %(mainchar)s to leave %(group)s." msgid "Rejected application from %(mainchar)s to leave %(group)s."
msgstr "%(mainchar)s离开%(group)s的请求已被拒绝" msgstr "%(mainchar)s离开%(group)s的请求已被拒绝"
#: allianceauth/groupmanagement/views.py:336 #: allianceauth/groupmanagement/views.py:339
#: allianceauth/groupmanagement/views.py:346 #: allianceauth/groupmanagement/views.py:349
msgid "You cannot join that group" msgid "You cannot join that group"
msgstr "你无法加入那个用户组" msgstr "你无法加入那个用户组"
#: allianceauth/groupmanagement/views.py:341 #: allianceauth/groupmanagement/views.py:344
msgid "You are already a member of that group." msgid "You are already a member of that group."
msgstr "你已经是那个群组的一员了。" msgstr "你已经是那个群组的一员了。"
#: allianceauth/groupmanagement/views.py:358 #: allianceauth/groupmanagement/views.py:361
msgid "You already have a pending application for that group." msgid "You already have a pending application for that group."
msgstr "你已经有了该组的未决申请" msgstr "你已经有了该组的未决申请"
#: allianceauth/groupmanagement/views.py:367 #: allianceauth/groupmanagement/views.py:370
#, python-format #, python-format
msgid "Applied to group %(group)s." msgid "Applied to group %(group)s."
msgstr "修改已经应用到%(group)s啦" msgstr "修改已经应用到%(group)s啦"
#: allianceauth/groupmanagement/views.py:377 #: allianceauth/groupmanagement/views.py:380
msgid "You cannot leave that group" msgid "You cannot leave that group"
msgstr "你无法离开那个用户组" msgstr "你无法离开那个用户组"
#: allianceauth/groupmanagement/views.py:381 #: allianceauth/groupmanagement/views.py:384
msgid "You are not a member of that group" msgid "You are not a member of that group"
msgstr "你不是那个用户组的成员" msgstr "你不是那个用户组的成员"
#: allianceauth/groupmanagement/views.py:393 #: allianceauth/groupmanagement/views.py:396
msgid "You already have a pending leave request for that group." msgid "You already have a pending leave request for that group."
msgstr "你已经有了该组的未决离开请求" msgstr "你已经有了该组的未决离开请求"
#: allianceauth/groupmanagement/views.py:409 #: allianceauth/groupmanagement/views.py:412
#, python-format #, python-format
msgid "Applied to leave group %(group)s." msgid "Applied to leave group %(group)s."
msgstr "已经离开群组%(group)s" msgstr "已经离开群组%(group)s"
@ -1079,16 +1120,6 @@ msgstr "创建申请"
msgid "Username" msgid "Username"
msgstr "用户名" msgstr "用户名"
#: allianceauth/hrapplications/templates/hrapplications/management.html:28
#: allianceauth/hrapplications/templates/hrapplications/management.html:83
#: allianceauth/hrapplications/templates/hrapplications/management.html:127
#: allianceauth/hrapplications/templates/hrapplications/searchview.html:27
#: allianceauth/hrapplications/templates/hrapplications/view.html:73
#: allianceauth/srp/templates/srp/data.html:101
#: allianceauth/srp/templates/srp/management.html:44
msgid "Actions"
msgstr "操作"
#: allianceauth/hrapplications/templates/hrapplications/management.html:38 #: allianceauth/hrapplications/templates/hrapplications/management.html:38
#: allianceauth/hrapplications/templates/hrapplications/management.html:99 #: allianceauth/hrapplications/templates/hrapplications/management.html:99
#: allianceauth/hrapplications/templates/hrapplications/management.html:143 #: allianceauth/hrapplications/templates/hrapplications/management.html:143
@ -1427,10 +1458,6 @@ msgstr "类型"
msgid "Code Name" msgid "Code Name"
msgstr "操作类型" msgstr "操作类型"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:35
msgid "Users"
msgstr "用户"
#: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41 #: allianceauth/permissions_tool/templates/permissions_tool/overview.html:41
msgid "States" msgid "States"
msgstr "声望" msgstr "声望"
@ -2153,11 +2180,11 @@ msgid ""
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:95 #: allianceauth/templates/allianceauth/admin-status/overview.html:95
#, python-format msgid "running"
msgid "" msgstr ""
"\n"
" %(queue_length)s queued tasks\n" #: allianceauth/templates/allianceauth/admin-status/overview.html:96
" " msgid "queued"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/top-menu-admin.html:9 #: allianceauth/templates/allianceauth/top-menu-admin.html:9
@ -2173,11 +2200,11 @@ msgid "AA Support Discord"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:10
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:14 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:16
msgid "User Menu" msgid "User Menu"
msgstr "" msgstr ""
#: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:56 #: allianceauth/templates/allianceauth/top-menu-user-dropdown.html:67
msgid "Logout" msgid "Logout"
msgstr "登出" msgstr "登出"
@ -2233,22 +2260,30 @@ msgid "Objective"
msgstr "声望" msgstr "声望"
#: allianceauth/timerboard/form.py:64 #: allianceauth/timerboard/form.py:64
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
msgid "Days Remaining" msgid "Days Remaining"
msgstr "剩余天数" msgstr "剩余天数"
#: allianceauth/timerboard/form.py:65 #: allianceauth/timerboard/form.py:67
msgid "Hours Remaining" msgid "Hours Remaining"
msgstr "剩余小时数" msgstr "剩余小时数"
#: allianceauth/timerboard/form.py:67 #: allianceauth/timerboard/form.py:69
msgid "Minutes Remaining" msgid "Minutes Remaining"
msgstr "剩余分钟" msgstr "剩余分钟"
#: allianceauth/timerboard/form.py:69 #: allianceauth/timerboard/form.py:71
msgid "Important" msgid "Important"
msgstr "重要信息" msgstr "重要信息"
#: allianceauth/timerboard/form.py:70 #: allianceauth/timerboard/form.py:72
msgid "Corp-Restricted" msgid "Corp-Restricted"
msgstr "受限制的公司" msgstr "受限制的公司"

View File

@ -1,9 +1,111 @@
"""Admin site for menu app."""
from typing import Optional
from django.contrib import admin from django.contrib import admin
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_noop as _
from . import models from .constants import MenuItemType
from .core.smart_sync import sync_menu
from .filters import MenuItemTypeListFilter
from .forms import (
AppMenuItemAdminForm,
FolderMenuItemAdminForm,
LinkMenuItemAdminForm,
)
from .models import MenuItem
@admin.register(models.MenuItem) @admin.register(MenuItem)
class MenuItemAdmin(admin.ModelAdmin): class MenuItemAdmin(admin.ModelAdmin):
list_display = ['text', 'hide', 'parent', 'url', 'icon_classes', 'rank'] list_display = (
ordering = ('rank',) "_text",
"parent",
"order",
"_user_defined",
"_visible",
"_children",
)
list_filter = [
MenuItemTypeListFilter,
"is_hidden",
("parent", admin.RelatedOnlyFieldListFilter),
]
ordering = ["parent", "order", "text"]
def get_form(self, request: HttpRequest, obj: Optional[MenuItem] = None, **kwargs):
kwargs["form"] = self._choose_form(request, obj)
return super().get_form(request, obj, **kwargs)
@classmethod
def _choose_form(cls, request: HttpRequest, obj: Optional[MenuItem]):
"""Return the form for the current menu item type."""
if obj: # change
if obj.hook_hash:
return AppMenuItemAdminForm
if obj.is_folder:
return FolderMenuItemAdminForm
return LinkMenuItemAdminForm
# add
if cls._type_from_request(request) is MenuItemType.FOLDER:
return FolderMenuItemAdminForm
return LinkMenuItemAdminForm
def add_view(self, request, form_url="", extra_context=None) -> HttpResponse:
context = extra_context or {}
item_type = self._type_from_request(request, default=MenuItemType.LINK)
context["title"] = _("Add %s menu item") % item_type.label
return super().add_view(request, form_url, context)
def change_view(
self, request, object_id, form_url="", extra_context=None
) -> HttpResponse:
extra_context = extra_context or {}
obj = get_object_or_404(MenuItem, id=object_id)
extra_context["title"] = _("Change %s menu item") % obj.item_type.label
return super().change_view(request, object_id, form_url, extra_context)
def changelist_view(self, request: HttpRequest, extra_context=None):
# needed to ensure items are updated after an app change
# and when the admin page is opened directly
sync_menu()
extra_context = extra_context or {}
extra_context["folder_type"] = MenuItemType.FOLDER.value
return super().changelist_view(request, extra_context)
@admin.display(description=_("children"))
def _children(self, obj: MenuItem):
if not obj.is_folder:
return []
names = [obj.text for obj in obj.children.order_by("order", "text")]
return names if names else "?"
@admin.display(description=_("text"), ordering="text")
def _text(self, obj: MenuItem) -> str:
if obj.is_folder:
return f"[{obj.text}]"
return obj.text
@admin.display(description=_("user defined"), boolean=True)
def _user_defined(self, obj: MenuItem) -> bool:
return obj.is_user_defined
@admin.display(description=_("visible"), ordering="is_hidden", boolean=True)
def _visible(self, obj: MenuItem) -> bool:
return not bool(obj.is_hidden)
@staticmethod
def _type_from_request(
request: HttpRequest, default=None
) -> Optional[MenuItemType]:
try:
return MenuItemType(request.GET.get("type"))
except ValueError:
return default

View File

@ -1,19 +1,19 @@
import logging import logging
from django.apps import AppConfig from django.apps import AppConfig
from django.db.utils import ProgrammingError, OperationalError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# TODO discuss permissions for user defined links
# TODO define aa way for hooks to predefine a "parent" to create a sub menu from modules
# TODO Add user documentation
class MenuConfig(AppConfig): class MenuConfig(AppConfig):
name = "allianceauth.menu" name = "allianceauth.menu"
label = "menu" label = "menu"
def ready(self): def ready(self):
try: from allianceauth.menu.core import smart_sync
from allianceauth.menu.providers import menu_provider
menu_provider.clear_synced_flag() smart_sync.reset_menu_items_sync()
except (ProgrammingError, OperationalError):
logger.warning("Migrations not completed for MenuItems")

View File

@ -0,0 +1,18 @@
"""Global constants for the menu app."""
from django.db import models
from django.utils.translation import gettext_lazy as _
DEFAULT_FOLDER_ICON_CLASSES = "fa-solid fa-folder" # TODO: Make this a setting?
"""Default icon class for folders."""
DEFAULT_MENU_ITEM_ORDER = 9999
"""Default order for any menu item."""
class MenuItemType(models.TextChoices):
"""The type of a menu item."""
APP = "app", _("app")
FOLDER = "folder", _("folder")
LINK = "link", _("link")

View File

@ -0,0 +1,48 @@
"""Logic for handling MenuItemHook objects."""
import hashlib
from typing import List, NamedTuple, Optional
from allianceauth.menu.hooks import MenuItemHook
class MenuItemHookCustom(MenuItemHook):
"""A user defined menu item that can be rendered with the standard template."""
def __init__(
self,
text: str,
classes: str,
url_name: str,
order: Optional[int] = None,
navactive: Optional[List[str]] = None,
):
super().__init__(text, classes, url_name, order, navactive)
self.url = ""
self.is_folder = None
self.html_id = ""
self.children = []
class MenuItemHookParams(NamedTuple):
"""Immutable container for params about a menu item hook."""
text: str
order: int
hash: str
def generate_hash(obj: MenuItemHook) -> str:
"""Return the hash for a menu item hook."""
my_class = obj.__class__
name = f"{my_class.__module__}.{my_class.__name__}"
hash_value = hashlib.sha256(name.encode("utf-8")).hexdigest()
return hash_value
def gather_params(obj: MenuItemHook) -> MenuItemHookParams:
"""Return params from a menu item hook."""
text = getattr(obj, "text", obj.__class__.__name__)
order = getattr(obj, "order", None)
hash = generate_hash(obj)
return MenuItemHookParams(text=text, hash=hash, order=order)

View File

@ -0,0 +1,30 @@
"""Provide capability to sync menu items when needed only."""
from django.core.cache import cache
_MENU_SYNC_CACHE_KEY = "ALLIANCEAUTH-MENU-SYNCED"
def sync_menu() -> None:
"""Sync menu items if needed only."""
from allianceauth.menu.models import MenuItem
is_sync_needed = not _is_menu_synced() or not MenuItem.objects.exists()
# need to also check for existence of MenuItems in database
# to ensure the menu is synced during tests
if is_sync_needed:
MenuItem.objects.sync_all()
_record_menu_was_synced()
def _is_menu_synced() -> bool:
return cache.get(_MENU_SYNC_CACHE_KEY, False)
def _record_menu_was_synced() -> None:
cache.set(_MENU_SYNC_CACHE_KEY, True, timeout=None) # no timeout
def reset_menu_items_sync() -> None:
"""Ensure menu items are synced, e.g. after a Django restart."""
cache.delete(_MENU_SYNC_CACHE_KEY)

View File

@ -0,0 +1,24 @@
"""Filters for the menu app."""
from django.contrib import admin
from django.utils.translation import gettext_noop as _
from allianceauth.menu.constants import MenuItemType
class MenuItemTypeListFilter(admin.SimpleListFilter):
"""Allow filtering admin changelist by menu item type."""
title = _("type")
parameter_name = "type"
def lookups(self, request, model_admin):
return [(obj.value, obj.label.title()) for obj in MenuItemType]
def queryset(self, request, queryset):
if value := self.value():
return queryset.annotate_item_type_2().filter(
item_type_2=MenuItemType(value).value
)
return None

View File

@ -0,0 +1,49 @@
from django import forms
from .constants import DEFAULT_FOLDER_ICON_CLASSES
from .models import MenuItem
class FolderMenuItemAdminForm(forms.ModelForm):
"""A form for changing folder items."""
class Meta:
model = MenuItem
fields = ["text", "classes", "order", "is_hidden"]
def clean(self):
data = super().clean()
if not data["classes"]:
data["classes"] = DEFAULT_FOLDER_ICON_CLASSES
return data
class _BasedMenuItemAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["parent"].queryset = MenuItem.objects.filter_folders().order_by(
"text"
)
self.fields["parent"].required = False
self.fields["parent"].widget = forms.Select(
choices=self.fields["parent"].widget.choices
) # disable modify buttons
class AppMenuItemAdminForm(_BasedMenuItemAdminForm):
"""A form for changing app items."""
class Meta:
model = MenuItem
fields = ["order", "parent", "is_hidden"]
class LinkMenuItemAdminForm(_BasedMenuItemAdminForm):
"""A form for changing link items."""
class Meta:
model = MenuItem
fields = ["text", "url", "classes", "order", "parent", "is_hidden"]
widgets = {
"url": forms.TextInput(attrs={"size": "100"}),
}

View File

@ -1,42 +1,58 @@
from django.template.loader import render_to_string """Menu item hooks."""
from typing import List, Optional from typing import List, Optional
from django.template.loader import render_to_string
from allianceauth.menu.constants import DEFAULT_MENU_ITEM_ORDER
class MenuItemHook: class MenuItemHook:
""" """Auth Hook for generating side menu items.
Auth Hook for generating Side Menu Items
""" Args:
def __init__(self, text: str, classes: str, url_name: str, order: Optional[int] = None, navactive: List = []): - text: The text shown as menu item, e.g. usually the name of the app.
""" - classes: The classes that should be applied to the menu item icon
:param text: The text shown as menu item, e.g. usually the name of the app. - url_name: The name of the Django URL to use
:type text: str - order: An integer which specifies the order of the menu item,
:param classes: The classes that should be applied to the menu item icon lowest to highest. Community apps are free to use any order above `1000`.
:type classes: List[str] Numbers below are served for Auth.
:param url_name: The name of the Django URL to use - A list of views or namespaces the link should be highlighted on.
:type url_name: str See 3rd party package django-navhelper for usage.
:param order: An integer which specifies the order of the menu item, lowest to highest. Community apps are free to use any order above `1000`. Numbers below are served for Auth. Defaults to the supplied `url_name`.
:type order: Optional[int], optional
:param navactive: A list of views or namespaces the link should be highlighted on. See [django-navhelper](https://github.com/geelweb/django-navhelper#navactive) for usage. Defaults to the supplied `url_name`.
:type navactive: List, optional Optional:
- count is an integer shown next to the menu item as badge when is is not `None`.
Apps need to set the count in their child class, e.g. in `render()` method
""" """
def __init__(
self,
text: str,
classes: str,
url_name: str,
order: Optional[int] = None,
navactive: Optional[List[str]] = None,
):
self.text = text self.text = text
self.classes = classes self.classes = classes
self.url_name = url_name self.url_name = url_name
self.template = 'public/menuitem.html' self.template = "public/menuitem.html"
self.order = order if order is not None else 9999 self.order = order if order is not None else DEFAULT_MENU_ITEM_ORDER
# count is an integer shown next to the menu item as badge when count != None
# apps need to set the count in their child class, e.g. in render() method
self.count = None self.count = None
navactive = navactive or [] navactive = navactive or []
navactive.append(url_name) navactive.append(url_name)
self.navactive = navactive self.navactive = navactive
def render(self, request): def __str__(self) -> str:
return render_to_string(self.template, return self.text
{'item': self},
request=request) def __repr__(self) -> str:
return f'{self.__class__.__name__}(text="{self.text}")'
def render(self, request) -> str:
"""Render this menu item and return resulting HTML."""
return render_to_string(self.template, {"item": self}, request=request)

View File

@ -0,0 +1,67 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from django.db import models
from django.db.models import Case, Q, Value, When
from allianceauth.hooks import get_hooks
from .constants import MenuItemType
from .core.menu_item_hooks import MenuItemHookParams, gather_params
if TYPE_CHECKING:
from .models import MenuItem
logger = logging.getLogger(__name__)
class MenuItemQuerySet(models.QuerySet):
def filter_folders(self):
"""Add filter to include folders only."""
return self.filter(hook_hash__isnull=True, url="")
def annotate_item_type_2(self):
"""Add calculated field with item type."""
return self.annotate(
item_type_2=Case(
When(~Q(hook_hash__isnull=True), then=Value(MenuItemType.APP.value)),
When(url="", then=Value(MenuItemType.FOLDER.value)),
default=Value(MenuItemType.LINK.value),
)
)
class MenuItemManagerBase(models.Manager):
def sync_all(self):
"""Sync all menu items from hooks."""
hook_params = self._gather_menu_item_hook_params()
self._delete_obsolete_app_items(hook_params)
self._update_or_create_app_items(hook_params)
def _gather_menu_item_hook_params(self) -> list[MenuItemHookParams]:
params = [gather_params(hook()) for hook in get_hooks("menu_item_hook")]
return params
def _delete_obsolete_app_items(self, params: list[MenuItemHookParams]):
hashes = [obj.hash for obj in params]
self.exclude(hook_hash__isnull=True).exclude(hook_hash__in=hashes).delete()
def _update_or_create_app_items(self, params: list[MenuItemHookParams]):
for param in params:
try:
obj: MenuItem = self.get(hook_hash=param.hash)
except self.model.DoesNotExist:
self.create(hook_hash=param.hash, order=param.order, text=param.text)
else:
# if it exists update the text only
if obj.text != param.text:
obj.text = param.text
obj.save()
logger.debug("Updated menu items from %d menu item hooks", len(params))
MenuItemManager = MenuItemManagerBase.from_queryset(MenuItemQuerySet)

View File

@ -1,15 +0,0 @@
from django.utils.deprecation import MiddlewareMixin
import logging
from allianceauth.menu.providers import menu_provider
logger = logging.getLogger(__name__)
class MenuSyncMiddleware(MiddlewareMixin):
def __call__(self, request):
"""Alliance Auth Menu Sync Middleware"""
menu_provider.check_and_sync_menu()
return super().__call__(request)

View File

@ -1,4 +1,4 @@
# Generated by Django 4.0.2 on 2022-08-28 14:00 # Generated by Django 4.2.9 on 2024-02-15 00:01
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -8,21 +8,88 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = []
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='MenuItem', name="MenuItem",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('hook_function', models.CharField(max_length=500)), "id",
('icon_classes', models.CharField(max_length=150)), models.AutoField(
('text', models.CharField(max_length=150)), auto_created=True,
('url', models.CharField(blank=True, default=None, max_length=2048, null=True)), primary_key=True,
('rank', models.IntegerField(default=1000)), serialize=False,
('hide', models.BooleanField(default=False)), verbose_name="ID",
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='menu.menuitem')), ),
),
(
"text",
models.CharField(
db_index=True,
help_text="Text to show on menu",
max_length=150,
verbose_name="text",
),
),
(
"order",
models.IntegerField(
db_index=True,
default=9999,
help_text="Order of the menu. Lowest First",
verbose_name="order",
),
),
(
"is_hidden",
models.BooleanField(
default=False,
help_text="Hide this menu item.If this item is a folder all items under it will be hidden too",
verbose_name="is hidden",
),
),
(
"hook_hash",
models.CharField(
default=None,
editable=False,
max_length=64,
null=True,
unique=True,
),
),
(
"classes",
models.CharField(
blank=True,
default="",
help_text="Font Awesome classes to show as icon on menu, e.g. <code>fa-solid fa-house</code>",
max_length=150,
verbose_name="icon classes",
),
),
(
"url",
models.TextField(
default="",
help_text="External URL this menu items will link to",
verbose_name="url",
),
),
(
"parent",
models.ForeignKey(
blank=True,
default=None,
help_text="Folder this item is in (optional)",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="children",
to="menu.menuitem",
verbose_name="folder",
),
),
], ],
), ),
] ]

View File

@ -1,28 +0,0 @@
# Generated by Django 4.0.2 on 2022-08-28 14:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('menu', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='menuitem',
name='hook_function',
field=models.CharField(blank=True, default=None, max_length=500, null=True),
),
migrations.AlterField(
model_name='menuitem',
name='icon_classes',
field=models.CharField(blank=True, default=None, max_length=150, null=True),
),
migrations.AlterField(
model_name='menuitem',
name='text',
field=models.CharField(blank=True, default=None, max_length=150, null=True),
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 4.0.8 on 2023-02-05 07:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('menu', '0002_alter_menuitem_hook_function_and_more'),
]
operations = [
migrations.AddIndex(
model_name='menuitem',
index=models.Index(fields=['rank'], name='menu_menuit_rank_e880ab_idx'),
),
]

View File

@ -1,39 +0,0 @@
# Generated by Django 4.0.10 on 2023-07-16 11:41
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('menu', '0003_menuitem_menu_menuit_rank_e880ab_idx'),
]
operations = [
migrations.AlterField(
model_name='menuitem',
name='hide',
field=models.BooleanField(default=False, help_text='Hide this menu item. If this item is a header all items under it will be hidden too.'),
),
migrations.AlterField(
model_name='menuitem',
name='icon_classes',
field=models.CharField(blank=True, default=None, help_text='Font Awesome classes to show as icon on menu', max_length=150, null=True),
),
migrations.AlterField(
model_name='menuitem',
name='parent',
field=models.ForeignKey(blank=True, help_text='Parent Header. (Optional)', null=True, on_delete=django.db.models.deletion.SET_NULL, to='menu.menuitem'),
),
migrations.AlterField(
model_name='menuitem',
name='rank',
field=models.IntegerField(default=1000, help_text='Order of the menu. Lowest First.'),
),
migrations.AlterField(
model_name='menuitem',
name='text',
field=models.CharField(blank=True, default=None, help_text='Text to show on menu', max_length=150, null=True),
),
]

View File

@ -1,174 +1,132 @@
import logging
from allianceauth.hooks import get_hooks
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
logger = logging.getLogger(__name__) from allianceauth.menu.constants import DEFAULT_FOLDER_ICON_CLASSES
from .constants import DEFAULT_MENU_ITEM_ORDER, MenuItemType
from .core.menu_item_hooks import MenuItemHookCustom
from .managers import MenuItemManager
class MenuItem(models.Model): class MenuItem(models.Model):
# Auto Generated model from an auth_hook """An item in the sidebar menu.
hook_function = models.CharField(
max_length=500, default=None, null=True, blank=True) Some of these objects are generated from `MenuItemHook` objects.
To avoid confusion we are using the same same field names.user defined
"""
# User Made Model
icon_classes = models.CharField(
max_length=150, default=None, null=True, blank=True, help_text="Font Awesome classes to show as icon on menu")
text = models.CharField( text = models.CharField(
max_length=150, default=None, null=True, blank=True, help_text="Text to show on menu") max_length=150,
url = models.CharField(max_length=2048, default=None, db_index=True,
null=True, blank=True) verbose_name=_("text"),
help_text=_("Text to show on menu"),
# Put it under a header? )
order = models.IntegerField(
default=DEFAULT_MENU_ITEM_ORDER,
db_index=True,
verbose_name=_("order"),
help_text=_("Order of the menu. Lowest First"),
)
parent = models.ForeignKey( parent = models.ForeignKey(
'self', on_delete=models.SET_NULL, null=True, blank=True, help_text="Parent Header. (Optional)") "self",
on_delete=models.SET_NULL,
default=None,
null=True,
blank=True,
related_name="children",
verbose_name=_("folder"),
help_text=_("Folder this item is in (optional)"),
)
is_hidden = models.BooleanField(
default=False,
verbose_name=_("is hidden"),
help_text=_(
"Hide this menu item."
"If this item is a folder all items under it will be hidden too"
),
)
# Put it where? lowest first # app related properties
rank = models.IntegerField(default=1000, help_text="Order of the menu. Lowest First.") hook_hash = models.CharField(
max_length=64, default=None, null=True, unique=True, editable=False
) # hash of a menu item hook. Must be nullable for unique comparison.
# Hide it fully? Hiding a parent will hide all it's children # user defined properties
hide = models.BooleanField(default=False, help_text="Hide this menu item. If this item is a header all items under it will be hidden too.") classes = models.CharField(
max_length=150,
default="",
blank=True,
verbose_name=_("icon classes"),
help_text=_(
"Font Awesome classes to show as icon on menu, "
"e.g. <code>fa-solid fa-house</code>"
),
)
url = models.TextField(
default="",
verbose_name=_("url"),
help_text=_("External URL this menu items will link to"),
)
class Meta: objects = MenuItemManager()
indexes = [
models.Index(fields=['rank', ]),
]
def __str__(self) -> str: def __str__(self) -> str:
return self.text return self.text
def save(self, *args, **kwargs):
if not self.hook_hash:
self.hook_hash = None # empty strings can create problems
return super().save(*args, **kwargs)
@property @property
def classes(self): # Helper function to make this model closer to the hook functions def item_type(self) -> MenuItemType:
return self.icon_classes """Return the type of this menu item."""
if self.hook_hash:
return MenuItemType.APP
@staticmethod if not self.url:
def hook_to_name(mh): return MenuItemType.FOLDER
return f"{mh.__class__.__module__}.{mh.__class__.__name__}"
@staticmethod return MenuItemType.LINK
def sync_hook_models():
# TODO define aa way for hooks to predefine a "parent" to create a sub menu from modules @property
menu_hooks = get_hooks('menu_item_hook') def is_app_item(self) -> bool:
hook_functions = [] """Return True if this is an app item, else False."""
for hook in menu_hooks: return self.item_type is MenuItemType.APP
mh = hook()
cls = MenuItem.hook_to_name(mh) @property
try: def is_child(self) -> bool:
# if it exists update the text only """Return True if this item is a child, else False."""
# Users can adjust ranks so lets not change it if they have. return bool(self.parent_id)
mi = MenuItem.objects.get(hook_function=cls)
mi.text = getattr(mh, "text", mh.__class__.__name__) @property
mi.save() def is_folder(self) -> bool:
except MenuItem.DoesNotExist: """Return True if this item is a folder, else False."""
# This is a new hook, Make the database model. return self.item_type is MenuItemType.FOLDER
MenuItem.objects.create(
hook_function=cls, @property
rank=getattr(mh, "order", 500), def is_link_item(self) -> bool:
text=getattr(mh, "text", mh.__class__.__name__) """Return True if this item is a link item, else False."""
return self.item_type is MenuItemType.LINK
@property
def is_user_defined(self) -> bool:
"""Return True if this item is user defined."""
return self.item_type is not MenuItemType.APP
def to_hook_obj(self) -> MenuItemHookCustom:
"""Convert to hook object for rendering."""
if self.is_app_item:
raise ValueError("The related hook objects should be used for app items.")
hook_obj = MenuItemHookCustom(
text=self.text, classes=self.classes, url_name="", order=self.order
) )
hook_functions.append(cls) hook_obj.navactive = []
if self.is_folder and not self.classes:
hook_obj.classes = DEFAULT_FOLDER_ICON_CLASSES
# Get rid of any legacy hooks from modules removed hook_obj.url = self.url
MenuItem.objects.filter(hook_function__isnull=False).exclude( hook_obj.is_folder = self.is_folder
hook_function__in=hook_functions).delete() hook_obj.html_id = f"id-folder-{self.id}" if self.is_folder else ""
return hook_obj
@classmethod
def filter_items(cls, menu_item: dict):
"""
filter any items with no valid children from a menu
"""
count_items = len(menu_item['items'])
if count_items: # if we have children confirm we can see them
for i in menu_item['items']:
if len(i['render']) == 0:
count_items -= 1
if count_items == 0: # no children left dont render header
return False
return True
else:
return True
@classmethod
def render_menu(cls, request):
"""
Return the sorted side menu items with any items the user can't see removed.
"""
# Override all the items to the bs5 theme
template = "menu/menu-item-bs5.html"
# TODO discuss permissions for user defined links
# Turn all the hooks into functions
menu_hooks = get_hooks('menu_item_hook')
items = {}
for fn in menu_hooks:
f = fn()
items[cls.hook_to_name(f)] = f
menu_items = MenuItem.objects.all().order_by("rank")
menu = {}
for mi in menu_items:
if mi.hide:
# hidden item, skip it completely
continue
try:
_cnt = 0
_render = None
if mi.hook_function:
# This is a module hook, so we need to render it as the developer intended
# TODO add a new attribute for apps that want to override it in the new theme
items[mi.hook_function].template = template
_render = items[mi.hook_function].render(request)
_cnt = items[mi.hook_function].count
else:
# This is a user defined menu item so we render it with defaults.
_render = render_to_string(template,
{'item': mi},
request=request)
parent = mi.id
if mi.parent_id: # Set it if present
parent = mi.parent_id
if parent not in menu: # this will cause the menu headers to be out of order
menu[parent] = {"items": [],
"count": 0,
"render": None,
"text": "None",
"rank": 9999,
}
_mi = {
"count": _cnt,
"render": _render,
"text": mi.text,
"rank": mi.rank,
"classes": (mi.icon_classes if mi.icon_classes != "" else "fa-solid fa-folder"),
"hide": mi.hide
}
if parent != mi.id:
# this is a sub item
menu[parent]["items"].append(_mi)
if _cnt:
#add its count to the header count
menu[parent]["count"] += _cnt
else:
if len(menu[parent]["items"]):
# this is a top folder dont update the count.
del(_mi["count"])
menu[parent].update(_mi)
except Exception as e:
logger.exception(e)
# reset to list
menu = list(menu.values())
# sort the menu list as the parents may be out of order.
menu.sort(key=lambda i: i['rank'])
# ensure no empty groups
menu = filter(cls.filter_items, menu)
return menu

View File

@ -1,49 +0,0 @@
import logging
from django.core.cache import cache
from allianceauth.menu.models import MenuItem
from allianceauth.utils.django import StartupCommand
logger = logging.getLogger(__name__)
MENU_SYNC_CACHE_KEY = "ALLIANCEAUTH-MENU-SYNCED"
MENU_CACHE_KEY = "ALLIANCEAUTH-MENU-CACHE"
class MenuProvider():
def clear_synced_flag(self) -> bool:
return cache.delete(MENU_SYNC_CACHE_KEY)
def set_synced_flag(self) -> bool:
return cache.set(MENU_SYNC_CACHE_KEY, True)
def get_synced_flag(self) -> bool:
return cache.get(MENU_SYNC_CACHE_KEY, False)
def sync_menu_models(self):
MenuItem.sync_hook_models()
self.set_synced_flag()
def check_and_sync_menu(self) -> None:
if self.get_synced_flag():
# performance hit to each page view to ensure tests work.
# tests clear DB but not cache.
# TODO rethink all of this?
if MenuItem.objects.all().count() > 0:
logger.debug("Menu Hooks Synced")
else:
self.sync_menu_models()
else:
logger.debug("Syncing Menu Hooks")
self.sync_menu_models()
def get_and_cache_menu(self):
pass
def clear_menu_cache(self):
pass
menu_provider = MenuProvider()

View File

@ -0,0 +1,13 @@
{% extends "admin/change_list.html" %}
{% load i18n %}
{% block object-tools-items %}
{{ block.super }}
<li>
<a href="{% url 'admin:menu_menuitem_add' %}?type={{ folder_type }}" class="addlink">
{% translate "Add folder" %}
</a>
</li>
{% endblock %}

View File

@ -1,7 +1,3 @@
{% for data in menu_items %} {% for item in menu_items %}
{% if data.items|length > 0 %} {{ item.html }}
{% include "menu/menu-item-bs5.html" with item=data %}
{% else %}
{{ data.render }}
{% endif %}
{% endfor %} {% endfor %}

View File

@ -1,37 +1,57 @@
{% load i18n %} {% load i18n %}
{% load navactive %} {% load navactive %}
{% if not item.hide %}
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0"> <li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
<i class="nav-link {{ item.classes }} fa-fw align-self-center me-3 {% if item.navactive %}{% navactive request item.navactive|join:' ' %}{% endif %}" {% if item.items|length %} type="button" data-bs-toggle="collapse" data-bs-target="#id-{{ item.text|slugify }}" aria-expanded="false" aria-controls="" {% endif %}></i> <i
<a class="nav-link flex-fill align-self-center" {% if item.items|length %} type="button" data-bs-toggle="collapse" data-bs-target="#id-{{ item.text|slugify }}" aria-expanded="false" aria-controls="" {% endif %} class="nav-link {{ item.classes }} fa-fw align-self-center me-3 {% if item.navactive %}{% navactive request item.navactive|join:' ' %}{% endif %}"
{% if item.is_folder %}
type="button"
data-bs-toggle="collapse"
data-bs-target="#{{ item.html_id }}"
aria-expanded="false"
aria-controls=""
{% endif %}>
</i>
<a
class="nav-link flex-fill align-self-center me-auto"
{% if item.is_folder %}
type="button"
data-bs-toggle="collapse"
data-bs-target="#{{ item.html_id }}"
aria-expanded="false"
aria-controls=""
{% endif %}
href="{% if item.url_name %}{% url item.url_name %}{% else %}{{ item.url }}{% endif %}"> href="{% if item.url_name %}{% url item.url_name %}{% else %}{{ item.url }}{% endif %}">
{% translate item.text %} {% translate item.text %}
</a> </a>
{% if item.count >= 1 %} {% if item.count >= 1 %}
<span class="badge bg-primary m-2 align-self-center {% if item.items|length == 0 %}me-4{% endif %}"> <span class="badge bg-primary m-2 align-self-center{% if not item.is_folder %} me-2{% endif %}">
{{ item.count }} {{ item.count }}
</span> </span>
{% elif item.url %} {% elif item.url %}
<span class="pill m-2 me-4 align-self-center fas fa-external-link-alt"></span> <span class="pill m-2 me-4 align-self-center fas fa-external-link-alt"></span>
{% endif %} {% endif %}
{% if item.items|length > 0 %} {% if item.is_folder %}
<span <span
class="pill m-2 me-4 align-self-center fas fa-solid fa-chevron-down" class="pill m-2 align-self-center collapsed"
type="button" type="button"
data-bs-toggle="collapse" data-bs-toggle="collapse"
data-bs-target="#id-{{ item.text|slugify }}" data-bs-target="#{{ item.html_id }}"
aria-expanded="false" aria-expanded="false"
aria-controls=""> aria-controls=""
>
<i class="fas fa-chevron-right"></i>
<i class="fas fa-chevron-down"></i>
</span> </span>
<!--<hr class="m-0 w-100">--> <ul
<ul class="collapse ps-1 w-100 border-start rounded-start border-light border-3" id="id-{{ item.text|slugify }}"> class="collapse ps-1 w-100 border-start rounded-start border-light border-3"
{% for sub_item in item.items %} id="{{ item.html_id }}">
{{ sub_item.render }} {% for sub_item in item.children %}
{{ sub_item }}
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
</li> </li>
{% endif %}

View File

@ -0,0 +1,3 @@
<div class="align-items-center text-center">
{% include "bundles/image-auth-logo.html" %}
</div>

View File

@ -2,57 +2,89 @@
{% load evelinks %} {% load evelinks %}
{% load theme_tags %} {% load theme_tags %}
<div style="z-index:5;" class="w100 d-flex flex-column justify-content-center align-items-center text-center pb-2"> <div id="aa-user-info" class="w-100 d-flex flex-column justify-content-center align-items-center text-center py-1 border-top border-secondary {% if not user.is_authenticated %}position-absolute bottom-0{% endif %}">
{% if user.is_authenticated %} <div class="d-flex mb-0 w-100">
{% if request.user.profile.main_character %}
{% with request.user.profile.main_character as main %}
<div class="p-2 position-relative m-2"> <div class="p-2 position-relative m-2">
{% if user.is_authenticated %}
{% with request.user.profile.main_character as main %}
<img class="rounded-circle" src="{{ main.character_id|character_portrait_url:64 }}" alt="{{ main.character_name }}"> <img class="rounded-circle" src="{{ main.character_id|character_portrait_url:64 }}" alt="{{ main.character_name }}">
<img class="rounded-circle position-absolute bottom-0 start-0" src="{{ main.corporation_logo_url_32 }}" alt="{{ main.corporation_name }}"> <img class="rounded-circle position-absolute bottom-0 start-0" src="{{ main.corporation_logo_url_32 }}" alt="{{ main.corporation_name }}">
{% if main.alliance_id %} {% if main.alliance_id %}
<img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.alliance_logo_url_32 }}" alt="{{ main.alliance_name }}"> <img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.alliance_logo_url_32 }}" alt="{{ main.alliance_name }}">
{% elif main.faction_id %} {% elif main.faction_id %}
<img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.faction_logo_url_32 }}" alt="{{ main.faction_name }}"> <img class="rounded-circle position-absolute bottom-0 end-0" src="{{ main.faction_logo_url_32 }}" alt="{{ main.faction_name }}">
{% endif %} {% endif %}
</div>
<h5>{{ main.character_name }}</h5>
{% endwith %} {% endwith %}
{% else %} {% else %}
<img class="rounded-circle m-2" src="{{ 1|character_portrait_url:32 }}" alt="{% translate 'No Main Character!' %}"> {% include "bundles/image-auth-logo.html" with logo_width="64px" %}
<h5>{% translate "No Main Character!" %}</h5>
{% endif %}
{% theme_select %}
{% endif %}
<div class="btn-group m-2">
<button type="button" class="btn btn-secondary p-1">
{% include "public/lang_select.html" %}
</button>
{% if user.is_superuser %}
<a role="button" class="btn btn btn-secondary d-flex" href="{% url 'admin:index' %}">
<span class="align-self-center">{% translate "Admin" %}</span>
</a>
{% endif %} {% endif %}
</div> </div>
<div class="align-self-center text-start">
<div class="btn-group m-2">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a role="button" class="btn btn-info" href="{% url 'authentication:token_management' %}" title="Token Management"><i class="fa-solid fa-user-lock fa-fw"></i></a> {% with request.user.profile.main_character as main %}
<h5 class="m-0">{{ main.character_name }}</h5>
<p class="m-0 small">{{ main.corporation_name }}</p>
{% if main.alliance_id %}
<p class="m-0 small">{{ main.alliance_name }}</p>
{% elif main.faction_id %}
<p class="m-0 small">{{ main.faction_name }}</p>
{% endif %} {% endif %}
{% endwith %}
{% else %}
<h5 class="m-0">{{ SITE_NAME }}</h5>
{% endif %}
</div>
<div class="ms-auto dropup">
<button type="button" class="h-100 btn" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-solid fa-gear fa-fw text-light"></i>
</button>
<ul class="dropdown-menu" style="min-width: 200px;">
<li><h6 class="dropdown-header">{% translate "Language" %}</h6></li>
<li>
<a class="dropdown-item">{% include "public/lang_select.html" %}</a>
</li>
<li><h6 class="dropdown-header">{% translate "Theme" %}</h6></li>
<li>
<a class="dropdown-item">
{% theme_select %}
</a>
</li>
{% if user.is_superuser %} {% if user.is_superuser %}
<a role="button" class="btn btn-info" href="https://allianceauth.readthedocs.io/" title="Alliance Auth Documentation"><i class="fa-solid fa-book fa-fw"></i></a> <li><hr class="dropdown-divider"></li>
<a role="button" class="btn btn-info" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i></a> <li><h6 class="dropdown-header">{% translate "Super User" %}</h6></li>
<a role="button" class="btn btn-info" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i></a> <li>
<a class="dropdown-item" href="https://allianceauth.readthedocs.io/" title="Alliance Auth Documentation"><i class="fa-solid fa-book fa-fw"></i> Alliance Auth Documentation</a>
</li>
<li>
<a class="dropdown-item" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i> Alliance Auth Discord</a>
</li>
<li>
<a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %}
</a>
</li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a role="button" class="btn btn-danger" href="{% url 'logout' %}" title="{% translate 'Sign Out' %}"><i class="fa-solid fa-right-from-bracket fa-fw"></i></a> <li>
<a class="dropdown-item" href="{% url 'authentication:token_management' %}">
<i class="fa-solid fa-user-lock fa-fw"></i> Token Management
</a>
</li>
<li>
<a class="dropdown-item text-danger" href="{% url 'logout' %}" title="{% translate 'Sign Out' %}"><i class="fa-solid fa-right-from-bracket fa-fw "></i> {% translate 'Sign Out' %}</a>
</li>
{% else %} {% else %}
<a role="button" class="btn btn-success" href="{% url 'authentication:login' %}" title="{% translate 'Sign In' %}"> <i class="fa-solid fa-right-to-bracket fa-fw"></i></a> <li>
<a class="dropdown-item text-success" href="{% url 'authentication:login' %}" title="{% translate 'Sign In' %}"> <i class="fa-solid fa-right-to-bracket fa-fw "></i> {% translate 'Sign In' %}</a>
</li>
{% endif %} {% endif %}
</ul>
</div>
</div> </div>
</div> </div>

View File

@ -4,10 +4,10 @@
<div class="col-auto px-0"> <div class="col-auto px-0">
<div class="collapse collapse-horizontal" tabindex="-1" id="sidebar"> <div class="collapse collapse-horizontal" tabindex="-1" id="sidebar">
<div style="width: 350px;"> <div>
<div class="nav-padding navbar-dark bg-dark text-light px-0 d-flex flex-column overflow-hidden vh-100 auth-logo"> <div class="nav-padding navbar-dark bg-dark text-light px-0 d-flex flex-column overflow-hidden vh-100 {% if not user.is_authenticated %}position-relative{% endif %}">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<ul style="z-index:5;" id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2"> <ul id="sidebar-menu" class="navbar-nav flex-column mb-auto overflow-auto pt-2">
<li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0"> <li class="d-flex flex-wrap m-2 p-2 pt-0 pb-0 mt-0 mb-0 me-0 pe-0">
<i class="nav-link fas fa-tachometer-alt fa-fw align-self-center me-3 {% navactive request 'authentication:dashboard' %}"></i> <i class="nav-link fas fa-tachometer-alt fa-fw align-self-center me-3 {% navactive request 'authentication:dashboard' %}"></i>
<a class="nav-link flex-fill align-self-center" href="{% url 'authentication:dashboard' %}"> <a class="nav-link flex-fill align-self-center" href="{% url 'authentication:dashboard' %}">
@ -15,8 +15,10 @@
</a> </a>
</li> </li>
{% sorted_menu_items %} {% menu_items %}
</ul> </ul>
{% include 'menu/menu-logo.html' %}
{% endif %} {% endif %}
{% include 'menu/menu-user.html' %} {% include 'menu/menu-user.html' %}

View File

@ -0,0 +1,32 @@
"""Template tags for rendering the classic side menu."""
from django import template
from django.http import HttpRequest
from allianceauth.hooks import get_hooks
register = template.Library()
# TODO: Show user created menu items
# TODO: Apply is_hidden feature for BS3 type items
@register.inclusion_tag("public/menublock.html", takes_context=True)
def menu_items(context: dict) -> dict:
"""Render menu items for classic dashboard."""
items = render_menu(context["request"])
return {"menu_items": items}
def render_menu(request: HttpRequest):
"""Return the rendered side menu for including in a template.
This function is creating a BS3 style menu.
"""
hooks = get_hooks("menu_item_hook")
raw_items = [fn() for fn in hooks]
raw_items.sort(key=lambda i: i.order)
menu_items = [item.render(request) for item in raw_items]
return menu_items

View File

@ -1,34 +1,174 @@
"""Template tags for rendering the new side menu.
Documentation of the render logic
---------------------------------
The are 3 types of menu items:
- App entries: Generated by hooks from Django apps
- Link entries: Linking to external pages. User created.
- Folder: Grouping together several app or link entries. User created.
The MenuItem model holds the current list of all menu items.
App entries are linked to a `MenuItemHook` object in the respective Django app.
Those hook objects contain dynamic logic in a `render()` method,
which must be executed when rendering for the current request.
Since the same template must be used to render all items, link entries and folders
are converted to `MenuItemHookCustom` objects, a sub class of `MenuItemHook`.
This ensures the template only rendered objects of one specific type or sub-type.
The rendered menu items are finally collected in a list of RenderedMenuItem objects,
which is used to render the complete menu.
"""
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from django import template from django import template
from django.db.models import QuerySet
from django.http import HttpRequest
from allianceauth.hooks import get_hooks from allianceauth.hooks import get_hooks
from allianceauth.menu.core import menu_item_hooks, smart_sync
from allianceauth.menu.models import MenuItem from allianceauth.menu.models import MenuItem
from allianceauth.services.auth_hooks import MenuItemHook
register = template.Library() register = template.Library()
def process_menu_items(hooks, request): @register.inclusion_tag("menu/menu-block.html", takes_context=True)
_menu_items = list() def menu_items(context: dict) -> dict:
items = [fn() for fn in hooks] """Render menu items for new dashboards."""
items.sort(key=lambda i: i.order) smart_sync.sync_menu()
for item in items:
_menu_items.append(item.render(request)) items = render_menu(context["request"])
return _menu_items return {"menu_items": items}
@register.inclusion_tag('public/menublock.html', takes_context=True) @dataclass
def menu_items(context): class RenderedMenuItem:
request = context['request'] """A rendered menu item.
return { These objects can be rendered with the menu-block template.
'menu_items': process_menu_items(get_hooks('menu_item_hook'), request), """
}
menu_item: MenuItem
children: List["RenderedMenuItem"] = field(default_factory=list)
count: Optional[int] = None
html: str = ""
html_id: str = ""
@property
def is_folder(self) -> bool:
"""Return True if this item is a folder."""
return self.menu_item.is_folder
def update_html(self, request: HttpRequest, template: str):
"""Render this menu item with defaults and set HTML ID."""
hook_obj = self.menu_item.to_hook_obj()
hook_obj.template = template
hook_obj.count = self.count
if self.is_folder:
hook_obj.children = [child.html for child in self.children]
self.html = hook_obj.render(request)
self.html_id = hook_obj.html_id
@register.inclusion_tag('menu/menu-block.html', takes_context=True) def render_menu(request: HttpRequest) -> List[RenderedMenuItem]:
def sorted_menu_items(context): """Return the rendered side menu for including in a template.
request = context['request']
menu_items = MenuItem.render_menu(request) This function is creating BS5 style menus.
return { """
'menu_items':menu_items hook_items = _gather_menu_items_from_hooks()
}
# Menu items needs to be rendered with the new BS5 template
bs5_template = "menu/menu-item-bs5.html"
rendered_items: Dict[int, RenderedMenuItem] = {}
menu_items: QuerySet[MenuItem] = MenuItem.objects.order_by(
"parent", "order", "text"
)
for item in menu_items:
if item.is_hidden:
continue # do not render hidden items
if item.is_app_item:
rendered_item = _render_app_item(request, hook_items, item, bs5_template)
elif item.is_link_item:
rendered_item = _render_link_item(request, item, bs5_template)
elif item.is_folder:
rendered_item = RenderedMenuItem(item) # we render these items later
else:
raise NotImplementedError("Unknown menu item type")
if item.is_child:
try:
parent = rendered_items[item.parent_id]
except KeyError:
continue # do not render children of hidden folders
parent.children.append(rendered_item)
if rendered_item.count is not None:
if parent.count is None:
parent.count = 0
parent.count += rendered_item.count
else:
rendered_items[item.id] = rendered_item
_remove_empty_folders(rendered_items)
_render_folder_items(request, rendered_items, bs5_template)
return list(rendered_items.values())
def _gather_menu_items_from_hooks() -> Dict[str, MenuItemHook]:
hook_items = {}
for hook in get_hooks("menu_item_hook"):
f = hook()
hook_items[menu_item_hooks.generate_hash(f)] = f
return hook_items
def _render_app_item(
request: HttpRequest, hook_items: dict, item: MenuItem, new_template: str
) -> RenderedMenuItem:
# This is a module hook, so we need to render it as the developer intended
# TODO add a new attribute for apps that want to override it in the new theme
hook_item = hook_items[item.hook_hash]
hook_item.template = new_template
html = hook_item.render(request)
count = hook_item.count
rendered_item = RenderedMenuItem(menu_item=item, count=count, html=html)
return rendered_item
def _render_link_item(
request: HttpRequest, item: MenuItem, new_template: str
) -> RenderedMenuItem:
rendered_item = RenderedMenuItem(menu_item=item)
rendered_item.update_html(request, template=new_template)
return rendered_item
def _render_folder_items(
request: HttpRequest, rendered_items: Dict[int, RenderedMenuItem], new_template: str
):
for item in rendered_items.values():
if item.menu_item.is_folder:
item.update_html(request=request, template=new_template)
def _remove_empty_folders(rendered_items: Dict[int, RenderedMenuItem]):
ids_to_remove = []
for item_id, item in rendered_items.items():
if item.is_folder and not item.children:
ids_to_remove.append(item_id)
for item_id in ids_to_remove:
del rendered_items[item_id]

View File

View File

View File

@ -0,0 +1,63 @@
from django.test import TestCase
from allianceauth.menu.core.menu_item_hooks import (
MenuItemHookCustom,
gather_params,
generate_hash,
)
from allianceauth.menu.tests.factories import create_menu_item_hook_function
class TestGenerateHash(TestCase):
def test_should_generate_same_hash(self):
# given
hook = create_menu_item_hook_function()
# when
result_1 = generate_hash(hook())
result_2 = generate_hash(hook())
# then
self.assertIsInstance(result_1, str)
self.assertEqual(result_1, result_2)
def test_should_generate_different_hashes(self):
# given
hook_1 = create_menu_item_hook_function()
hook_2 = create_menu_item_hook_function()
# when
result_1 = generate_hash(hook_1())
result_2 = generate_hash(hook_2())
# then
self.assertNotEqual(result_1, result_2)
class TestExtractParams(TestCase):
def test_should_return_params(self):
# given
hook = create_menu_item_hook_function(text="Alpha", order=42)
# when
result = gather_params(hook())
# then
self.assertEqual(result.text, "Alpha")
self.assertEqual(result.order, 42)
self.assertIsInstance(result.hash, str)
class TestMenuItemHookCustom(TestCase):
def test_should_create_minimal(self):
# when
obj = MenuItemHookCustom(text="text", classes="classes", url_name="url_name")
# then
self.assertEqual(obj.text, "text")
self.assertEqual(obj.classes, "classes")
self.assertEqual(obj.url_name, "url_name")
self.assertEqual(obj.url, "")
self.assertIsNone(obj.is_folder)
self.assertEqual(obj.html_id, "")
self.assertListEqual(obj.children, [])

View File

@ -0,0 +1,42 @@
from unittest.mock import patch
from django.test import TestCase
from allianceauth.menu.core import smart_sync
from allianceauth.menu.tests.factories import create_link_menu_item
from allianceauth.menu.tests.utils import PACKAGE_PATH
@patch(PACKAGE_PATH + ".models.MenuItem.objects.sync_all", spec=True)
class TestSmartSync(TestCase):
def test_should_sync_after_reset(self, mock_sync_all):
# given
smart_sync.reset_menu_items_sync()
mock_sync_all.reset_mock()
# when
smart_sync.sync_menu()
# then
self.assertTrue(mock_sync_all.called)
def test_should_sync_when_sync_flag_is_set_but_no_items_in_db(self, mock_sync_all):
# given
smart_sync._record_menu_was_synced()
# when
smart_sync.sync_menu()
# then
self.assertTrue(mock_sync_all.called)
def test_should_not_sync_when_sync_flag_is_set_and_items_in_db(self, mock_sync_all):
# given
smart_sync._record_menu_was_synced()
create_link_menu_item()
# when
smart_sync.sync_menu()
# then
self.assertFalse(mock_sync_all.called)

View File

@ -0,0 +1,95 @@
from itertools import count
from django.contrib.auth.models import User
from allianceauth.menu.core import menu_item_hooks
from allianceauth.menu.models import MenuItem
from allianceauth.menu.templatetags.menu_menu_items import RenderedMenuItem
from allianceauth.services.auth_hooks import MenuItemHook
from allianceauth.tests.auth_utils import AuthUtils
def create_user(permissions=None, **kwargs) -> User:
num = next(counter_user)
params = {"username": f"test_user_{num}"}
params.update(kwargs)
user = User.objects.create(**params)
if permissions:
user = AuthUtils.add_permissions_to_user_by_name(perms=permissions, user=user)
return user
def create_menu_item_hook(**kwargs) -> MenuItemHook:
num = next(counter_menu_item_hook)
new_class = type(f"GeneratedMenuItem{num}", (MenuItemHook,), {})
count = kwargs.pop("count", None)
params = {
"text": f"Dummy App #{num}",
"classes": "fa-solid fa-users-gear",
"url_name": "groupmanagement:management",
}
params.update(kwargs)
obj = new_class(**params)
for key, value in params.items():
setattr(obj, key, value)
obj.count = count
return obj
def create_menu_item_hook_function(**kwargs):
obj = create_menu_item_hook(**kwargs)
return lambda: obj
def create_link_menu_item(**kwargs) -> MenuItem:
num = next(counter_menu_item)
params = {
"url": f"https://www.example.com/{num}",
}
params.update(kwargs)
return _create_menu_item(**params)
def create_app_menu_item(**kwargs) -> MenuItem:
params = {"hook_hash": "hook_hash"}
params.update(kwargs)
return _create_menu_item(**params)
def create_folder_menu_item(**kwargs) -> MenuItem:
return _create_menu_item(**kwargs)
def create_menu_item_from_hook(hook, **kwargs) -> MenuItem:
item = hook()
hook_params = menu_item_hooks.gather_params(item)
params = {
"text": hook_params.text,
"hook_hash": hook_params.hash,
"order": hook_params.order,
}
params.update(kwargs)
return _create_menu_item(**params)
def _create_menu_item(**kwargs) -> MenuItem:
num = next(counter_menu_item)
params = {
"text": f"text #{num}",
}
params.update(kwargs)
return MenuItem.objects.create(**params)
def create_rendered_menu_item(**kwargs) -> RenderedMenuItem:
if "menu_item" not in kwargs:
kwargs["menu_item"] = create_link_menu_item()
return RenderedMenuItem(**kwargs)
counter_menu_item = count(1, 1)
counter_menu_item_hook = count(1, 1)
counter_user = count(1, 1)

View File

@ -0,0 +1,178 @@
from http import HTTPStatus
from django.test import TestCase
from django.urls import reverse
from allianceauth.menu.constants import MenuItemType
from allianceauth.menu.forms import (
AppMenuItemAdminForm,
FolderMenuItemAdminForm,
LinkMenuItemAdminForm,
)
from allianceauth.menu.models import MenuItem
from allianceauth.menu.tests.factories import (
create_app_menu_item,
create_folder_menu_item,
create_link_menu_item,
create_user,
)
from allianceauth.menu.tests.utils import extract_html
def extract_menu_item_texts(response):
"""Extract labels of menu items shown in change list."""
soup = extract_html(response)
items = soup.find_all("th", {"class": "field-_text"})
labels = {elem.text for elem in items}
return labels
class TestAdminSite(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.user = create_user(is_superuser=True, is_staff=True)
cls.changelist_url = reverse("admin:menu_menuitem_changelist")
cls.add_url = reverse("admin:menu_menuitem_add")
def change_url(self, id_):
return reverse("admin:menu_menuitem_change", args=[id_])
def test_changelist_should_show_all_types(self):
# given
self.client.force_login(self.user)
create_app_menu_item(text="app")
create_folder_menu_item(text="folder")
create_link_menu_item(text="link")
# when
response = self.client.get(self.changelist_url)
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
labels = extract_menu_item_texts(response)
self.assertSetEqual(labels, {"app", "[folder]", "link"})
def test_should_create_new_link_item(self):
# given
self.client.force_login(self.user)
# when
response = self.client.post(
self.add_url,
{"text": "alpha", "url": "http://www.example.com", "order": 99},
)
# then
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, self.changelist_url)
self.assertEqual(MenuItem.objects.count(), 1)
obj = MenuItem.objects.first()
self.assertEqual(obj.text, "alpha")
self.assertEqual(obj.item_type, MenuItemType.LINK)
def test_should_create_new_folder_item(self):
# given
self.client.force_login(self.user)
# when
response = self.client.post(
self.add_url + "?type=folder", {"text": "alpha", "order": 99}
)
# then
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, self.changelist_url)
self.assertEqual(MenuItem.objects.count(), 1)
obj = MenuItem.objects.first()
self.assertEqual(obj.text, "alpha")
self.assertEqual(obj.item_type, MenuItemType.FOLDER)
def test_should_change_app_item(self):
# given
self.client.force_login(self.user)
item = create_app_menu_item(text="alpha", order=1)
form_data = AppMenuItemAdminForm(instance=item).initial
form_data["order"] = 99
form_data["parent"] = ""
# when
response = self.client.post(self.change_url(item.id), form_data)
# then
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, self.changelist_url)
self.assertEqual(MenuItem.objects.count(), 1)
obj = MenuItem.objects.first()
self.assertEqual(obj.order, 99)
self.assertEqual(obj.item_type, MenuItemType.APP)
def test_should_change_link_item(self):
# given
self.client.force_login(self.user)
item = create_link_menu_item(text="alpha")
form_data = LinkMenuItemAdminForm(instance=item).initial
form_data["text"] = "bravo"
form_data["parent"] = ""
# when
response = self.client.post(self.change_url(item.id), form_data)
# then
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, self.changelist_url)
self.assertEqual(MenuItem.objects.count(), 1)
obj = MenuItem.objects.first()
self.assertEqual(obj.text, "bravo")
self.assertEqual(obj.item_type, MenuItemType.LINK)
def test_should_change_folder_item(self):
# given
self.client.force_login(self.user)
item = create_folder_menu_item(text="alpha")
form_data = FolderMenuItemAdminForm(instance=item).initial
form_data["text"] = "bravo"
# when
response = self.client.post(self.change_url(item.id), form_data)
# then
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, self.changelist_url)
self.assertEqual(MenuItem.objects.count(), 1)
obj = MenuItem.objects.first()
self.assertEqual(obj.text, "bravo")
self.assertEqual(obj.item_type, MenuItemType.FOLDER)
def test_should_move_item_into_folder(self):
# given
self.client.force_login(self.user)
link = create_link_menu_item(text="alpha")
folder = create_folder_menu_item(text="folder")
form_data = LinkMenuItemAdminForm(instance=link).initial
form_data["parent"] = folder.id
# when
response = self.client.post(self.change_url(link.id), form_data)
# then
self.assertEqual(response.status_code, HTTPStatus.FOUND)
self.assertEqual(response.url, self.changelist_url)
link.refresh_from_db()
self.assertEqual(link.parent, folder)
def test_should_filter_items_by_type(self):
# given
self.client.force_login(self.user)
create_app_menu_item(text="app")
create_folder_menu_item(text="folder")
create_link_menu_item(text="link")
# when
cases = [("link", "link"), ("app", "app"), ("folder", "[folder]")]
for filter_name, expected_label in cases:
with self.subTest(filter_name=filter_name):
response = self.client.get(self.changelist_url + f"?type={filter_name}")
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
labels = extract_menu_item_texts(response)
self.assertSetEqual(labels, {expected_label})

View File

@ -0,0 +1,102 @@
from http import HTTPStatus
from django.test import TestCase
from allianceauth.menu.core.smart_sync import reset_menu_items_sync
from allianceauth.menu.tests.factories import (
create_folder_menu_item,
create_link_menu_item,
create_user,
)
from allianceauth.menu.tests.utils import extract_links
class TestDefaultDashboardWithSideMenu(TestCase):
def test_should_show_all_types_of_menu_entries(self):
# given
user = create_user(permissions=["auth.group_management"])
self.client.force_login(user)
create_link_menu_item(text="Alpha", url="http://www.example.com/alpha")
folder = create_folder_menu_item(text="Folder")
create_link_menu_item(
text="Bravo", url="http://www.example.com/bravo", parent=folder
)
reset_menu_items_sync() # this simulates startup
# when
response = self.client.get("/dashboard/")
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
links = extract_links(response)
# open_page_in_browser(response)
self.assertEqual(links["/dashboard/"], "Dashboard")
self.assertEqual(links["/groups/"], "Groups")
self.assertEqual(links["/groupmanagement/requests/"], "Group Management")
self.assertEqual(links["http://www.example.com/alpha"], "Alpha")
self.assertEqual(links["http://www.example.com/bravo"], "Bravo")
def test_should_not_show_menu_entry_when_user_has_no_permission(self):
# given
user = create_user()
self.client.force_login(user)
reset_menu_items_sync()
# when
response = self.client.get("/dashboard/")
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
links = extract_links(response)
self.assertEqual(links["/dashboard/"], "Dashboard")
self.assertEqual(links["/groups/"], "Groups")
self.assertNotIn("/groupmanagement/requests/", links)
def test_should_not_show_menu_entry_when_hidden(self):
# given
user = create_user()
self.client.force_login(user)
create_link_menu_item(text="Alpha", url="http://www.example.com/")
reset_menu_items_sync()
# when
response = self.client.get("/dashboard/")
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
links = extract_links(response)
self.assertEqual(links["/dashboard/"], "Dashboard")
self.assertEqual(links["/groups/"], "Groups")
self.assertNotIn("http://www.example.com/alpha", links)
class TestBS3DashboardWithSideMenu(TestCase):
def test_should_not_show_group_management_when_user_has_no_permission(self):
# given
user = create_user()
self.client.force_login(user)
# when
response = self.client.get("/dashboard_bs3/")
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
links = extract_links(response)
self.assertEqual(links["/dashboard/"], "Dashboard")
self.assertEqual(links["/groups/"], "Groups")
self.assertNotIn("/groupmanagement/requests/", links)
def test_should_show_group_management_when_user_has_permission(self):
# given
user = create_user(permissions=["auth.group_management"])
self.client.force_login(user)
# when
response = self.client.get("/dashboard_bs3/")
# then
self.assertEqual(response.status_code, HTTPStatus.OK)
links = extract_links(response)
self.assertEqual(links["/dashboard/"], "Dashboard")
self.assertEqual(links["/groups/"], "Groups")
self.assertEqual(links["/groupmanagement/requests/"], "Group Management")

View File

@ -0,0 +1,54 @@
from unittest.mock import patch
from django.test import RequestFactory, TestCase
from allianceauth.menu.templatetags.menu_items import render_menu
from allianceauth.menu.tests.factories import create_menu_item_hook_function
from allianceauth.menu.tests.utils import PACKAGE_PATH, render_template
MODULE_PATH = PACKAGE_PATH + ".templatetags.menu_items"
class TestTemplateTags(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.factory = RequestFactory()
@patch(MODULE_PATH + ".render_menu", spec=True)
def test_menu_items(self, mock_render_menu):
# given
mock_render_menu.return_value = ["Alpha"]
request = self.factory.get("/")
# when
rendered = render_template(
"{% load menu_items %}{% menu_items %}",
context={"request": request},
)
self.assertIn("Alpha", rendered)
@patch(MODULE_PATH + ".get_hooks", spec=True)
class TestRenderMenu(TestCase):
def setUp(self) -> None:
self.factory = RequestFactory()
def test_should_render_menu_in_order(self, mock_get_hooks):
# given
mock_get_hooks.return_value = [
create_menu_item_hook_function(text="Charlie"),
create_menu_item_hook_function(text="Alpha", order=1),
create_menu_item_hook_function(text="Bravo", order=2),
]
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 3)
self.assertIn("Alpha", menu[0])
self.assertIn("Bravo", menu[1])
self.assertIn("Charlie", menu[2])

View File

@ -0,0 +1,326 @@
from typing import List, NamedTuple, Optional
from unittest.mock import patch
from bs4 import BeautifulSoup
from django.test import RequestFactory, TestCase
from allianceauth.menu.templatetags.menu_menu_items import (
RenderedMenuItem,
render_menu,
)
from allianceauth.menu.tests.factories import (
create_app_menu_item,
create_folder_menu_item,
create_link_menu_item,
create_menu_item_from_hook,
create_menu_item_hook_function,
create_rendered_menu_item,
)
from allianceauth.menu.tests.utils import (
PACKAGE_PATH,
remove_whitespaces,
render_template,
)
MODULE_PATH = PACKAGE_PATH + ".templatetags.menu_menu_items"
class TestTemplateTags(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.factory = RequestFactory()
@patch(MODULE_PATH + ".render_menu", spec=True)
@patch(MODULE_PATH + ".smart_sync.sync_menu", spec=True)
def test_sorted_menu_items(self, mock_sync_menu, mock_render_menu):
# given
fake_item = {"html": "Alpha"}
mock_render_menu.return_value = [fake_item]
request = self.factory.get("/")
# when
rendered = render_template(
"{% load menu_menu_items %}{% menu_items %}",
context={"request": request},
)
self.assertIn("Alpha", rendered)
self.assertTrue(mock_sync_menu.called)
@patch(MODULE_PATH + ".get_hooks", spec=True)
class TestRenderDefaultMenu(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.factory = RequestFactory()
def test_should_render_app_menu_items(self, mock_get_hooks):
# given
menu = [
create_menu_item_hook_function(text="Charlie", count=42),
create_menu_item_hook_function(text="Alpha", order=1),
create_menu_item_hook_function(text="Bravo", order=2),
]
mock_get_hooks.return_value = menu
for hook in menu:
create_menu_item_from_hook(hook)
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 3)
self.assertEqual(menu[0].menu_item.text, "Alpha")
self.assertEqual(menu[1].menu_item.text, "Bravo")
self.assertEqual(menu[2].menu_item.text, "Charlie")
self.assertEqual(menu[2].count, 42)
attrs = parse_html(menu[2])
self.assertEqual(attrs.count, 42)
self.assertEqual(attrs.text, "Charlie")
def test_should_render_link_menu_items(self, mock_get_hooks):
# given
mock_get_hooks.return_value = []
create_link_menu_item(text="Charlie"),
create_link_menu_item(text="Alpha", order=1),
create_link_menu_item(text="Bravo", order=2),
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 3)
self.assertEqual(menu[0].menu_item.text, "Alpha")
self.assertEqual(menu[1].menu_item.text, "Bravo")
self.assertEqual(menu[2].menu_item.text, "Charlie")
attrs = parse_html(menu[2])
self.assertEqual(attrs.text, "Charlie")
def test_should_render_folders(self, mock_get_hooks):
# given
mock_get_hooks.return_value = []
folder = create_folder_menu_item(text="Folder", order=2)
create_link_menu_item(text="Alpha", order=1)
create_link_menu_item(text="Bravo", order=3)
create_link_menu_item(text="Charlie", parent=folder)
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 3)
self.assertEqual(menu[0].menu_item.text, "Alpha")
self.assertEqual(menu[1].menu_item.text, "Folder")
self.assertEqual(menu[2].menu_item.text, "Bravo")
self.assertEqual(menu[1].children[0].menu_item.text, "Charlie")
attrs = parse_html(menu[1].children[0])
self.assertEqual(attrs.text, "Charlie")
def test_should_render_folder_properties(self, mock_get_hooks):
# given
# given
menu = [
create_menu_item_hook_function(text="Charlie", count=42),
create_menu_item_hook_function(text="Alpha", count=5),
create_menu_item_hook_function(text="Bravo"),
]
mock_get_hooks.return_value = menu
folder = create_folder_menu_item(text="Folder", order=1)
for hook in menu:
create_menu_item_from_hook(hook, parent=folder)
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 1)
item = menu[0]
self.assertEqual(item.menu_item.text, "Folder")
self.assertEqual(item.count, 47)
self.assertTrue(item.is_folder)
self.assertEqual(len(item.children), 3)
attrs = parse_html(item)
self.assertEqual(attrs.count, 47)
self.assertIn("fa-folder", attrs.classes)
def test_should_remove_empty_folders(self, mock_get_hooks):
# given
mock_get_hooks.return_value = []
create_folder_menu_item(text="Folder", order=2)
create_link_menu_item(text="Alpha", order=1)
create_link_menu_item(text="Bravo", order=3)
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 2)
self.assertEqual(menu[0].menu_item.text, "Alpha")
self.assertEqual(menu[1].menu_item.text, "Bravo")
def test_should_not_include_hidden_items(self, mock_get_hooks):
# given
mock_get_hooks.return_value = []
create_link_menu_item(text="Charlie"),
create_link_menu_item(text="Alpha", order=1),
create_link_menu_item(text="Bravo", order=2, is_hidden=True),
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 2)
self.assertEqual(menu[0].menu_item.text, "Alpha")
self.assertEqual(menu[1].menu_item.text, "Charlie")
def test_should_not_render_hidden_folders(self, mock_get_hooks):
# given
# given
menu = [
create_menu_item_hook_function(text="Charlie", count=42),
create_menu_item_hook_function(text="Alpha", count=5),
create_menu_item_hook_function(text="Bravo"),
]
mock_get_hooks.return_value = menu
folder = create_folder_menu_item(text="Folder", order=1, is_hidden=True)
for hook in menu:
create_menu_item_from_hook(hook, parent=folder)
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 0)
def test_should_allow_several_items_with_same_text(self, mock_get_hooks):
# given
mock_get_hooks.return_value = []
create_link_menu_item(text="Alpha", order=1),
create_link_menu_item(text="Alpha", order=2),
request = self.factory.get("/")
# when
result = render_menu(request)
# then
menu = list(result)
self.assertEqual(len(menu), 2)
self.assertEqual(menu[0].menu_item.text, "Alpha")
self.assertEqual(menu[1].menu_item.text, "Alpha")
class TestRenderedMenuItem(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.factory = RequestFactory()
cls.template = "menu/menu-item-bs5.html"
def test_create_from_menu_item_with_defaults(self):
# given
item = create_link_menu_item()
# when
obj = RenderedMenuItem(menu_item=item)
# then
self.assertEqual(obj.menu_item, item)
self.assertIsNone(obj.count)
self.assertEqual(obj.html, "")
self.assertEqual(obj.html_id, "")
self.assertListEqual(obj.children, [])
def test_should_identify_if_item_is_a_folder(self):
# given
app_item = create_rendered_menu_item(menu_item=create_app_menu_item())
link_item = create_rendered_menu_item(menu_item=create_link_menu_item())
folder_item = create_rendered_menu_item(menu_item=create_folder_menu_item())
cases = [
(app_item, False),
(link_item, False),
(folder_item, True),
]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertIs(obj.is_folder, expected)
def test_should_update_html_for_link_item(self):
# given
obj = create_rendered_menu_item(menu_item=create_link_menu_item(text="Alpha"))
request = self.factory.get("/")
# when
obj.update_html(request, self.template)
# then
parsed = parse_html(obj)
self.assertEqual(parsed.text, "Alpha")
self.assertIsNone(parsed.count)
self.assertFalse(obj.html_id)
def test_should_update_html_for_folder_item(self):
# given
request = self.factory.get("/")
folder_item = create_folder_menu_item(text="Alpha")
link_item = create_link_menu_item(text="Bravo", parent=folder_item)
obj = create_rendered_menu_item(menu_item=folder_item, count=42)
rendered_link = create_rendered_menu_item(menu_item=link_item)
rendered_link.update_html(request, self.template)
obj.children.append(rendered_link)
# when
obj.update_html(request, self.template)
# then
self.assertTrue(obj.html_id)
parsed_parent = parse_html(obj)
self.assertEqual(parsed_parent.text, "Alpha")
self.assertEqual(parsed_parent.count, 42)
self.assertIn("Bravo", obj.html)
class _ParsedMenuItem(NamedTuple):
classes: List[str]
text: str
count: Optional[int]
def parse_html(obj: RenderedMenuItem) -> _ParsedMenuItem:
soup = BeautifulSoup(obj.html, "html.parser")
classes = soup.li.i.attrs["class"]
text = remove_whitespaces(soup.li.a.text)
try:
count = int(remove_whitespaces(soup.li.span.text))
except (AttributeError, ValueError):
count = None
return _ParsedMenuItem(classes=classes, text=text, count=count)

View File

@ -0,0 +1,28 @@
from django.test import TestCase
from allianceauth.menu.constants import DEFAULT_FOLDER_ICON_CLASSES
from allianceauth.menu.forms import FolderMenuItemAdminForm
class TestFolderMenuItemAdminForm(TestCase):
def test_should_set_default_icon_classes(self):
# given
form_data = {"text": "Alpha", "order": 1}
form = FolderMenuItemAdminForm(data=form_data)
# when
obj = form.save(commit=False)
# then
self.assertEqual(obj.classes, DEFAULT_FOLDER_ICON_CLASSES)
def test_should_use_icon_classes_from_input(self):
# given
form_data = {"text": "Alpha", "order": 1, "classes": "dummy"}
form = FolderMenuItemAdminForm(data=form_data)
# when
obj = form.save(commit=False)
# then
self.assertEqual(obj.classes, "dummy")

View File

@ -0,0 +1,82 @@
from django.test import RequestFactory, TestCase
from allianceauth.menu.hooks import MenuItemHook
from .factories import create_menu_item_hook
class TestMenuItemHook(TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.factory = RequestFactory()
def test_should_create_obj_with_minimal_params(self):
# when
obj = MenuItemHook("text", "classes", "url-name")
# then
self.assertEqual(obj.text, "text")
self.assertEqual(obj.classes, "classes")
self.assertEqual(obj.url_name, "url-name")
self.assertEqual(obj.template, "public/menuitem.html")
self.assertEqual(obj.order, 9999)
self.assertListEqual(obj.navactive, ["url-name"])
self.assertIsNone(obj.count)
def test_should_create_obj_with_full_params_1(self):
# when
obj = MenuItemHook("text", "classes", "url-name", 5, ["navactive"])
# then
self.assertEqual(obj.text, "text")
self.assertEqual(obj.classes, "classes")
self.assertEqual(obj.url_name, "url-name")
self.assertEqual(obj.template, "public/menuitem.html")
self.assertEqual(obj.order, 5)
self.assertListEqual(obj.navactive, ["navactive", "url-name"])
self.assertIsNone(obj.count)
def test_should_create_obj_with_full_params_2(self):
# when
obj = MenuItemHook(
text="text",
classes="classes",
url_name="url-name",
order=5,
navactive=["navactive"],
)
# then
self.assertEqual(obj.text, "text")
self.assertEqual(obj.classes, "classes")
self.assertEqual(obj.url_name, "url-name")
self.assertEqual(obj.template, "public/menuitem.html")
self.assertEqual(obj.order, 5)
self.assertListEqual(obj.navactive, ["navactive", "url-name"])
self.assertIsNone(obj.count)
def test_should_render_menu_item(self):
# given
request = self.factory.get("/")
hook = create_menu_item_hook(text="Alpha")
# when
result = hook.render(request)
# then
self.assertIn("Alpha", result)
def test_str(self):
# given
hook = create_menu_item_hook(text="Alpha")
# when/then
self.assertEqual(str(hook), "Alpha")
def test_repr(self):
# given
hook = create_menu_item_hook(text="Alpha")
# when/then
self.assertIn("Alpha", repr(hook))

View File

@ -0,0 +1,103 @@
from unittest.mock import patch
from django.db.models import QuerySet
from django.test import TestCase
from allianceauth.menu.constants import MenuItemType
from allianceauth.menu.models import MenuItem
from .factories import (
create_app_menu_item,
create_folder_menu_item,
create_link_menu_item,
create_menu_item_from_hook,
create_menu_item_hook_function,
)
from .utils import PACKAGE_PATH
class TestMenuItemQuerySet(TestCase):
def test_should_add_item_type_field(self):
# given
app_item = create_app_menu_item()
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
# when
result: QuerySet[MenuItem] = MenuItem.objects.annotate_item_type_2()
# then
for obj in [app_item, link_item, folder_item]:
obj = result.get(pk=app_item.pk)
self.assertEqual(obj.item_type_2, obj.item_type)
def test_should_filter_folders(self):
# given
create_app_menu_item()
create_link_menu_item()
folder_item = create_folder_menu_item()
# when
result: QuerySet[MenuItem] = MenuItem.objects.filter_folders()
# then
item_pks = set(result.values_list("pk", flat=True))
self.assertSetEqual(item_pks, {folder_item.pk})
@patch(PACKAGE_PATH + ".managers.get_hooks", spec=True)
class TestMenuItemManagerSyncAll(TestCase):
def test_should_create_new_items_from_hooks_when_they_do_not_exist(
self, mock_get_hooks
):
# given
mock_get_hooks.return_value = [create_menu_item_hook_function(text="Alpha")]
# when
MenuItem.objects.sync_all()
# then
self.assertEqual(MenuItem.objects.count(), 1)
obj = MenuItem.objects.first()
self.assertEqual(obj.item_type, MenuItemType.APP)
self.assertEqual(obj.text, "Alpha")
def test_should_update_existing_app_items_when_changed_only(self, mock_get_hooks):
# given
menu_hook_1 = create_menu_item_hook_function(text="Alpha", order=1)
menu_hook_2 = create_menu_item_hook_function(text="Bravo", order=2)
mock_get_hooks.return_value = [menu_hook_1, menu_hook_2]
create_menu_item_from_hook(menu_hook_1, text="name has changed", order=99)
create_menu_item_from_hook(menu_hook_2)
# when
MenuItem.objects.sync_all()
# then
self.assertEqual(MenuItem.objects.count(), 2)
obj = MenuItem.objects.get(text="Alpha")
self.assertEqual(obj.item_type, MenuItemType.APP)
self.assertEqual(obj.order, 99)
obj = MenuItem.objects.get(text="Bravo")
self.assertEqual(obj.item_type, MenuItemType.APP)
self.assertEqual(obj.order, 2)
def test_should_remove_obsolete_app_items_but_keep_user_items(self, mock_get_hooks):
# given
menu_hook = create_menu_item_hook_function(text="Alpha")
mock_get_hooks.return_value = [menu_hook]
create_app_menu_item(text="Bravo") # obsolete item
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
# when
MenuItem.objects.sync_all()
# then
self.assertEqual(MenuItem.objects.count(), 3)
obj = MenuItem.objects.get(text="Alpha")
self.assertTrue(obj.item_type, MenuItemType.APP)
self.assertIn(link_item, MenuItem.objects.all())
self.assertIn(folder_item, MenuItem.objects.all())

View File

@ -0,0 +1,166 @@
from django.test import TestCase
from allianceauth.menu.constants import MenuItemType
from .factories import (
create_app_menu_item,
create_folder_menu_item,
create_link_menu_item,
)
class TestMenuItem(TestCase):
def test_str(self):
# given
obj = create_link_menu_item()
# when
result = str(obj)
# then
self.assertIsInstance(result, str)
def test_should_return_item_type(self):
# given
app_item = create_app_menu_item()
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
cases = [
(app_item, MenuItemType.APP),
(link_item, MenuItemType.LINK),
(folder_item, MenuItemType.FOLDER),
]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertEqual(obj.item_type, expected)
def test_should_identify_if_item_is_a_child(self):
# given
folder = create_folder_menu_item()
child = create_link_menu_item(parent=folder)
not_child = create_link_menu_item()
cases = [(child, True), (not_child, False)]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertIs(obj.is_child, expected)
def test_should_identify_if_item_is_a_folder(self):
# given
app_item = create_app_menu_item()
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
cases = [
(app_item, False),
(link_item, False),
(folder_item, True),
]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertIs(obj.is_folder, expected)
def test_should_identify_if_item_is_user_defined(self):
# given
app_item = create_app_menu_item()
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
cases = [
(app_item, False),
(link_item, True),
(folder_item, True),
]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertIs(obj.is_user_defined, expected)
def test_should_identify_if_item_is_an_app_item(self):
# given
app_item = create_app_menu_item()
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
cases = [
(app_item, True),
(link_item, False),
(folder_item, False),
]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertIs(obj.is_app_item, expected)
def test_should_identify_if_item_is_a_link_item(self):
# given
app_item = create_app_menu_item()
link_item = create_link_menu_item()
folder_item = create_folder_menu_item()
cases = [
(app_item, False),
(link_item, True),
(folder_item, False),
]
# when
for obj, expected in cases:
with self.subTest(type=expected):
self.assertIs(obj.is_link_item, expected)
def test_should_not_allow_creating_invalid_app_item(self):
# when
obj = create_app_menu_item(hook_hash="")
# then
obj.refresh_from_db()
self.assertIsNone(obj.hook_hash)
class TestMenuItemToHookObj(TestCase):
def test_should_create_from_link_item(self):
# given
obj = create_link_menu_item(text="Alpha")
# when
hook_obj = obj.to_hook_obj()
# then
self.assertEqual(hook_obj.text, "Alpha")
self.assertEqual(hook_obj.url, obj.url)
self.assertEqual(hook_obj.html_id, "")
self.assertFalse(hook_obj.is_folder)
def test_should_create_from_folder(self):
# given
obj = create_folder_menu_item(text="Alpha", classes="dummy")
# when
hook_obj = obj.to_hook_obj()
# then
self.assertEqual(hook_obj.text, "Alpha")
self.assertEqual(hook_obj.classes, "dummy")
self.assertEqual(hook_obj.url, "")
self.assertTrue(hook_obj.html_id)
self.assertTrue(hook_obj.is_folder)
def test_should_create_from_folder_and_use_default_icon_classes(self):
# given
obj = create_folder_menu_item(classes="")
# when
hook_obj = obj.to_hook_obj()
# then
self.assertEqual(hook_obj.classes, "fa-solid fa-folder")
def test_should_create_from_app_item(self):
# given
obj = create_app_menu_item(text="Alpha")
# when
with self.assertRaises(ValueError):
obj.to_hook_obj()

View File

@ -0,0 +1,47 @@
import tempfile
import webbrowser
from pathlib import Path
from bs4 import BeautifulSoup
from django.http import HttpResponse
from django.template import Context, Template
PACKAGE_PATH = "allianceauth.menu"
def extract_links(response: HttpResponse) -> dict:
soup = extract_html(response)
links = {
link["href"]: "".join(link.stripped_strings)
for link in soup.find_all("a", href=True)
}
return links
def extract_html(response: HttpResponse) -> BeautifulSoup:
soup = BeautifulSoup(response.content, "html.parser")
return soup
def open_page_in_browser(response: HttpResponse):
"""Open the response in the system's default browser.
This will create a temporary file in the user's home.
"""
path = Path.home() / "temp"
path.mkdir(exist_ok=True)
with tempfile.NamedTemporaryFile(dir=path, delete=False) as file:
file.write(response.content)
webbrowser.open(file.name)
def render_template(string, context=None):
context = context or {}
context = Context(context)
return Template(string).render(context)
def remove_whitespaces(s) -> str:
return s.replace("\n", "").strip()

View File

@ -11,16 +11,20 @@
{% translate "Fleet Operation Timers" %} {% translate "Fleet Operation Timers" %}
{% endblock header_nav_brand %} {% endblock header_nav_brand %}
{% block header_nav_collapse_right %}
{% if perms.auth.optimer_management %}
<li class="nav-item">
<a class="btn btn-success" href="{% url 'optimer:add' %}">
{% translate "Create Operation" %}
</a>
</li>
{% endif %}
{% endblock header_nav_collapse_right %}
{% block content %} {% block content %}
<div> <div>
<div class="text-end">
{% if perms.auth.optimer_management %}
<a href="{% url 'optimer:add' %}" class="btn btn-success">{% translate "Create Operation" %}</a>
{% endif %}
</div>
<div class="text-center mb-3"> <div class="text-center mb-3">
<div class="badge bg-info text-start"> <div class="badge bg-primary text-start">
<b>{% translate "Current Eve Time:" %}</b> <b>{% translate "Current Eve Time:" %}</b>
<span id="current-time"></span> <span id="current-time"></span>
</div> </div>

View File

@ -23,7 +23,7 @@
</p> </p>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped" id="tab_permissions_audit" style="width: 100%;"> <table class="table table-striped w-100" id="tab_permissions_audit">
<thead> <thead>
<tr> <tr>
<th scope="col">{% translate "Group" %}</th> <th scope="col">{% translate "Group" %}</th>
@ -55,7 +55,7 @@
{% endblock content %} {% endblock content %}
{% block extra_javascript %} {% block extra_javascript %}
{% include "bundles/datatables-js-bs5.html" %} {% include "bundles/datatables-js-bs5.html" %}
{# {% include "bundles/filterdropdown-js.html" %}#} {% include "bundles/filterdropdown-js.html" %}
<script> <script>
$(document).ready(() => { $(document).ready(() => {
@ -75,7 +75,8 @@
idx: 0, idx: 0,
title: 'Source' title: 'Source'
}], }],
bootstrap: true bootstrap: true,
bootstrap_version: 5
}, },
"stateSave": true, "stateSave": true,
"stateDuration": 0, "stateDuration": 0,

View File

@ -23,7 +23,7 @@
</p> </p>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped" id="tab_permissions_overview" style="width: 100%;"> <table class="table table-striped w-100" id="tab_permissions_overview">
<thead> <thead>
<tr> <tr>
<th scope="col">{% translate "App" %}</th> <th scope="col">{% translate "App" %}</th>
@ -60,7 +60,7 @@
{% block extra_javascript %} {% block extra_javascript %}
{% include "bundles/datatables-js-bs5.html" %} {% include "bundles/datatables-js-bs5.html" %}
{# {% include "bundles/filterdropdown-js.html" %}#} {% include "bundles/filterdropdown-js.html" %}
<script> <script>
$(document).ready(() => { $(document).ready(() => {
@ -86,6 +86,7 @@
} }
], ],
bootstrap: true, bootstrap: true,
bootstrap_version: 5
}, },
"stateSave": true, "stateSave": true,
"stateDuration": 0, "stateDuration": 0,

View File

@ -8,9 +8,10 @@ If you wish to make changes, overload the setting in your project's settings fil
import os import os
from django.contrib import messages
from celery.schedules import crontab from celery.schedules import crontab
from django.contrib import messages
INSTALLED_APPS = [ INSTALLED_APPS = [
'allianceauth', # needs to be on top of this list to support favicons in Django admin (see https://gitlab.com/allianceauth/allianceauth/-/issues/1301) 'allianceauth', # needs to be on top of this list to support favicons in Django admin (see https://gitlab.com/allianceauth/allianceauth/-/issues/1301)
'django.contrib.admin', 'django.contrib.admin',
@ -73,7 +74,6 @@ PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(PROJECT_DIR) BASE_DIR = os.path.dirname(PROJECT_DIR)
MIDDLEWARE = [ MIDDLEWARE = [
'allianceauth.menu.middleware.MenuSyncMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'allianceauth.authentication.middleware.UserSettingsMiddleware', 'allianceauth.authentication.middleware.UserSettingsMiddleware',
@ -179,7 +179,7 @@ MESSAGE_TAGS = {
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django_redis.cache.RedisCache", "BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1" # change the 1 here to change the database used "LOCATION": "redis://127.0.0.1:6379/1" # change the 1 here for the DB used
} }
} }
@ -212,6 +212,8 @@ LOGOUT_REDIRECT_URL = 'authentication:dashboard' # destination after logging ou
# scopes required on new tokens when logging in. Cannot be blank. # scopes required on new tokens when logging in. Cannot be blank.
LOGIN_TOKEN_SCOPES = ['publicData'] LOGIN_TOKEN_SCOPES = ['publicData']
EMAIL_TIMEOUT = 15
# number of days email verification links are valid for # number of days email verification links are valid for
ACCOUNT_ACTIVATION_DAYS = 1 ACCOUNT_ACTIVATION_DAYS = 1

View File

@ -23,7 +23,7 @@
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
{% if generated != "" %} {% if generated != "" %}
<div class="text-right mb-3"> <div class="text-end mb-3">
<textarea class="form-control" rows="10" cols="60">{{ generated }}</textarea> <textarea class="form-control" rows="10" cols="60">{{ generated }}</textarea>
</div> </div>
{% endif %} {% endif %}

View File

@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
<div class="card text-center m-3" style="min-width: 18rem; min-height: 18rem;"> <div class="card text-center m-2" style="min-width: 18rem; min-height: 18rem;">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{% block title %}{% endblock title %}</h5> <h5 class="card-title">{% block title %}{% endblock title %}</h5>

View File

@ -11,25 +11,33 @@
{% translate "Ship Replacement Program" %} {% translate "Ship Replacement Program" %}
{% endblock header_nav_brand %} {% endblock header_nav_brand %}
{% block header_nav_collapse_left %}
<a class="nav-link" href="{% url 'srp:management' %}">
{% translate "View Fleets" %}
</a>
{% endblock header_nav_collapse_left %}
{% block header_nav_collapse_right %}
{% if perms.auth.srp_management %}
<li class="nav-item">
{% if fleet_status == "Completed" %}
<a class="btn btn-warning" href="{% url 'srp:mark_uncompleted' fleet_id %}">
{% translate "Mark Incomplete" %}
</a>
{% else %}
<a class="btn btn-success" href="{% url 'srp:mark_completed' fleet_id %}">
{% translate "Mark Completed" %}
</a>
{% endif %}
</li>
{% endif %}
{% endblock header_nav_collapse_right %}
{% block content %} {% block content %}
<div> <div>
{% translate "SRP Fleet Data" as page_header %} {% translate "SRP Fleet Data" as page_header %}
{% include "framework/header/page-header.html" with title=page_header %} {% include "framework/header/page-header.html" with title=page_header %}
<div class="text-end mb-3">
{% if perms.auth.srp_management %}
{% if fleet_status == "Completed" %}
<a href="{% url 'srp:mark_uncompleted' fleet_id %}" class="btn btn-warning">
{% translate "Mark Incomplete" %}
</a>
{% else %}
<a href="{% url 'srp:mark_completed' fleet_id %}" class="btn btn-success">
{% translate "Mark Completed" %}
</a>
{% endif %}
{% endif %}
</div>
{% if srpfleetrequests %} {% if srpfleetrequests %}
<form method="POST"> <form method="POST">
{% csrf_token %} {% csrf_token %}

View File

@ -11,22 +11,26 @@
{% translate "Ship Replacement Program" %} {% translate "Ship Replacement Program" %}
{% endblock header_nav_brand %} {% endblock header_nav_brand %}
{% block content %} {% block header_nav_collapse_left %}
<div>
<div class="text-end mb-3">
{% if perms.auth.srp_management %} {% if perms.auth.srp_management %}
<a href="{% url 'srp:all' %}" class="btn btn-primary"> <li class="nav-item">
<a class="nav-link" href="{% url 'srp:all' %}">
{% translate "View All" %} {% translate "View All" %}
</a> </a>
</li>
{% endif %} {% endif %}
{% endblock header_nav_collapse_left %}
{% block header_nav_collapse_right %}
{% if perms.srp.add_srpfleetmain or perms.auth.srp_management %} {% if perms.srp.add_srpfleetmain or perms.auth.srp_management %}
<a href="{% url 'srp:add' %}" class="btn btn-success"> <li class="nav-item">
<a class="btn btn-success" href="{% url 'srp:add' %}">
{% translate "Add SRP Fleet" %} {% translate "Add SRP Fleet" %}
</a> </a>
</li>
{% endif %} {% endif %}
</div> {% endblock header_nav_collapse_right %}
{% block content %}
<div>
<div class="alert alert-info" role="alert"> <div class="alert alert-info" role="alert">
<div class="text-end"> <div class="text-end">
<b>{% translate "Total ISK Cost:" %} {{ totalcost | intcomma }}</b> <b>{% translate "Total ISK Cost:" %} {{ totalcost | intcomma }}</b>
@ -52,9 +56,7 @@
{% for srpfleet in srpfleets %} {% for srpfleet in srpfleets %}
<tr> <tr>
<td> <td>
<div class="badge bg-info">
{{ srpfleet.fleet_name }} {{ srpfleet.fleet_name }}
</div>
</td> </td>
<td>{{ srpfleet.fleet_time | date:"Y-m-d H:i" }}</td> <td>{{ srpfleet.fleet_time | date:"Y-m-d H:i" }}</td>
<td>{{ srpfleet.fleet_doctrine }}</td> <td>{{ srpfleet.fleet_doctrine }}</td>
@ -93,7 +95,7 @@
<td> <td>
<div class="badge bg-warning">{{ srpfleet.pending_requests }}</div> <div class="badge bg-warning">{{ srpfleet.pending_requests }}</div>
</td> </td>
<td class="text-end"> <td class="text-end text-nowrap">
<a href="{% url 'srp:fleet' srpfleet.id %}" class="btn btn-primary btn-sm m-1" title="View"> <a href="{% url 'srp:fleet' srpfleet.id %}" class="btn btn-primary btn-sm m-1" title="View">
<i class="fa-solid fa-eye"></i> <i class="fa-solid fa-eye"></i>
</a> </a>

View File

@ -1,239 +1,296 @@
/* /**
* filterDropDown.js * filterDropDown.js
* *
* Copyright (C) 2017-18 Erik Kalkoken * Copyright (C) 2017-24 Erik Kalkoken
* *
* Extension for the jQuery plug-in DataTables (developed and tested with v1.10.15) * Extension for the jQuery plug-in DataTables (developed and tested with v1.13.7)
* *
* Version 0.4.0 * Version 0.5.0
* **/
**/ (($) => {
"use strict";
(function ($) { /**
* Parse initialization array and returns filterDef array to faster and easy use,
// parse initialization array and returns filterDef array to faster and easy use * also sets defaults for properties that are not set
// also sets defaults for properties that are not set *
function parseInitArray(initArray) { * @param initArray
// initialization and setting defaults * @returns {{autoSize: boolean, bootstrap_version: number, columnsIdxList: *[], columns: *[], bootstrap: boolean, label: string, ajax: null}}
let filterDef = { */
"columns": [], const parseInitArray = (initArray) => {
"columnsIdxList": [], /**
"bootstrap": false, * Default filter definition
"autoSize": true, *
"ajax": null, * @type {{autoSize: boolean, bootstrap_version: number, columnsIdxList: *[], columns: *[], bootstrap: boolean, label: string, ajax: null}}
"label": "Filter " */
const filterDef = {
columns: [],
columnsIdxList: [],
bootstrap: false,
bootstrap_version: 3,
autoSize: true,
ajax: null,
label: "Filter ",
}; };
// set filter properties if they have been defined // Set filter properties if they have been defined otherwise the defaults will be used
// otherwise the defaults will be used if (
if (("bootstrap" in initArray) && (typeof initArray.bootstrap === 'boolean')) { "bootstrap" in initArray &&
typeof initArray.bootstrap === "boolean"
) {
filterDef.bootstrap = initArray.bootstrap; filterDef.bootstrap = initArray.bootstrap;
} }
if (("autoSize" in initArray) && (typeof initArray.autoSize === 'boolean')) { if (
"bootstrap_version" in initArray &&
typeof initArray.bootstrap_version === "number"
) {
filterDef.bootstrap_version = initArray.bootstrap_version;
}
if (
"autoSize" in initArray &&
typeof initArray.autoSize === "boolean"
) {
filterDef.autoSize = initArray.autoSize; filterDef.autoSize = initArray.autoSize;
} }
if (("ajax" in initArray) && (typeof initArray.ajax === 'string')) { if ("ajax" in initArray && typeof initArray.ajax === "string") {
filterDef.ajax = initArray.ajax; filterDef.ajax = initArray.ajax;
} }
if (("label" in initArray) && (typeof initArray.label === 'string')) { if ("label" in initArray && typeof initArray.label === "string") {
filterDef.label = initArray.label; filterDef.label = initArray.label;
} }
// add definition for each column // Add definition for each column
if ("columns" in initArray) { if ("columns" in initArray) {
initArray.columns.forEach(function (initColumn) { initArray.columns.forEach((initColumn) => {
if (("idx" in initColumn) && (typeof initColumn.idx === 'number')) { if ("idx" in initColumn && typeof initColumn.idx === "number") {
// initialize column // Initialize column
let idx = initColumn.idx; const idx = initColumn.idx;
filterDef['columns'][idx] = {
"title": null, filterDef.columns[idx] = {
"maxWidth": null, title: null,
"autoSize": true maxWidth: null,
autoSize: true,
}; };
// add to list of indices in same order they appear in the init array // Add to a list of indices in the same order they appear in the init array
filterDef['columnsIdxList'].push(idx); filterDef.columnsIdxList.push(idx);
// set column properties if they have been defined // Set column properties if they have been defined otherwise the defaults will be used
// otherwise the defaults will be used if (
if (('title' in initColumn) "title" in initColumn &&
&& (typeof initColumn.title === 'string') typeof initColumn.title === "string"
) { ) {
filterDef['columns'][idx].title = initColumn.title; filterDef.columns[idx].title = initColumn.title;
} }
if (('maxWidth' in initColumn) if (
&& (typeof initColumn.maxWidth === 'string') "maxWidth" in initColumn &&
typeof initColumn.maxWidth === "string"
) { ) {
filterDef['columns'][idx].maxWidth = initColumn.maxWidth; filterDef.columns[idx].maxWidth = initColumn.maxWidth;
} }
if (('autoSize' in initColumn) if (
&& (typeof initColumn.autoSize === 'boolean') "autoSize" in initColumn &&
typeof initColumn.autoSize === "boolean"
) { ) {
filterDef['columns'][idx].autoSize = initColumn.autoSize; filterDef.columns[idx].autoSize = initColumn.autoSize;
} }
} }
}); });
} }
return filterDef; return filterDef;
} };
// Add option d to given select object /**
function addOption(select, d) { * Add option d to the given select object
if (d != "") { *
select.append('<option value="' + d + '">' + d + '</option>'); * @param select
} * @param d
*/
const addOption = (select, d) => {
if (d !== "") {
select.append(`<option value="${d}">${d}</option>`);
} }
};
// initalizing select for current column and applying event to react to changes /**
function initSelectForColumn(id, column) { * Initialize the select element for given column and apply event to react to changes
let select = $("#" + id + "_filterSelect" + column.index()); *
select.on('change', function () { * @param id
let val = $.fn.dataTable.util.escapeRegex($(this).val()); * @param column
column * @returns {*|jQuery|HTMLElement}
.search(val ? '^' + val + '$' : '', true, false) */
.draw(); const initSelectForColumn = (id, column) => {
const select = $(`#${id}_filterSelect${column.index()}`);
$(select).change(() => {
const val = $.fn.dataTable.util.escapeRegex($(select).val());
column.search(val ? `^${val}$` : "", true, false).draw();
}); });
return select
return select;
};
// Add filterDropDown container div, draw select elements with default options.
// Use preInit so that elements are created and correctly shown before data is loaded
$(document).on("preInit.dt", (e, settings) => {
if (e.namespace !== "dt") {
return;
} }
// Add filterDropDown container div, draw select elements with default options // Get the api object for the current dt table
// use preInit so that elements are created and correctly shown before data is loaded const api = new $.fn.dataTable.Api(settings);
$(document).on('preInit.dt', function (e, settings) {
if (e.namespace !== 'dt') return;
// get api object for current dt table // Get the id of the current table
var api = new $.fn.dataTable.Api(settings); const id = api.table().node().id;
// get id of current table // Get the initialization object for the current table to retrieve custom settings
var id = api.table().node().id; const initObj = api.init();
// get initialization object for current table to retrieve custom settings // Only proceed if the filter has been defined in the current table,
var initObj = api.init(); // otherwise don't do anything.
if (!("filterDropDown" in initObj)) {
return;
}
// only proceed if filter has been defined in current table, otherwise don't do anything. // Get the current filter definition from the init array
if (!("filterDropDown" in initObj)) return; const filterDef = parseInitArray(initObj.filterDropDown);
// get current filter definition from init array
var filterDef = parseInitArray(initObj.filterDropDown);
// only proceed if there are any columns defined // only proceed if there are any columns defined
if (filterDef.columns.length == 0) return; if (filterDef.columns.length === 0) {
return;
}
// get container div for current data table to add new elements to // Get container div for the current data table to add new elements to
var container = api.table().container(); const container = api.table().container();
// Add filter elements to DOM
const filterWrapperId = `${id}_filterWrapper`;
// Set CSS classes for the filter wrapper div depending on bootstrap setting
let divCssClass = `${filterWrapperId} ${
filterDef.bootstrap ? "form-inline" : ""
}`;
if (filterDef.bootstrap && filterDef.bootstrap_version === 5) {
divCssClass = `${filterWrapperId} input-group my-3`;
}
// add filter elements to DOM
var filterWrapperId = id + "_filterWrapper";
var divCssClass = filterWrapperId + " " + (
(filterDef.bootstrap)
? "form-inline"
: ""
);
$(container).prepend( $(container).prepend(
'<div id="' `<div id="${filterWrapperId}" class="${divCssClass}"><span class="pt-2">${filterDef.label}</span></div>`
+ filterWrapperId
+ '" class="'
+ divCssClass + '">'
+ filterDef.label
+ '</div>'
); );
api.columns(filterDef.columnsIdxList).every(function () { api.columns(filterDef.columnsIdxList).every(function () {
let idx = this.index(); const idx = this.index();
// set title of current column // set title of current column
let colName = (filterDef.columns[idx].title !== null) let colName =
filterDef.columns[idx].title !== null
? filterDef.columns[idx].title ? filterDef.columns[idx].title
: $(this.header()).html(); : $(this.header()).html();
if (colName == "") colName = 'column ' + (idx + 1); if (colName === "") {
colName = `column ${idx + 1}`;
// adding select element for current column to container
let selectId = id + "_filterSelect" + idx;
$('#' + filterWrapperId).append(
'<select id="'
+ selectId
+ '" class="form-control '
+ id
+ '_filterSelect"></select>'
);
// initalizing select for current column and applying event to react to changes
let select = $("#" + selectId).empty()
.append('<option value="">(' + colName + ')</option>');
// set max width of select elements to current width (which is defined by the size of the title)
// turn off on for very small screens for responsive design or if autoSize has been set to false
if (filterDef.autoSize && filterDef.columns[idx].autoSize && (screen.width > 768)) {
select.css('max-width', select.outerWidth());
} }
// apply optional css style if defined in init array // Adding the select element for current column to container
// will override automatic max width setting const selectId = `${id}_filterSelect${idx}`;
// Set CSS classes for the select element depending on bootstrap setting
let selectMarkup = `<select id="${selectId}" class="form-control ${id}_filterSelect"></select>`;
if (filterDef.bootstrap && filterDef.bootstrap_version === 5) {
selectMarkup = `<select id="${selectId}" class="form-select w-auto ms-2 ${id}_filterSelect"></select>`;
}
$("#" + filterWrapperId).append(selectMarkup);
// Initializing select for current column and applying event to react to changes
const select = $("#" + selectId)
.empty()
.append(`<option value="">(${colName})</option>`);
// Set max width of select elements to current width (which is defined by the size of the title)
// Turn off on for very small screens for responsive design, or if autoSize has been set to false
if (
filterDef.autoSize &&
filterDef.columns[idx].autoSize &&
screen.width > 768
) {
select.css("max-width", select.outerWidth());
}
// Apply optional css style if defined in the init array will override automatic max width setting
if (filterDef.columns[idx].maxWidth !== null) { if (filterDef.columns[idx].maxWidth !== null) {
select.css('max-width', filterDef.columns[idx].maxWidth); select.css("max-width", filterDef.columns[idx].maxWidth);
} }
}); });
}); });
// filter table and add available options to dropDowns // Filter table and add available options to dropDowns
$(document).on('init.dt', function (e, settings) { $(document).on("init.dt", (e, settings) => {
if (e.namespace !== 'dt') return; if (e.namespace !== "dt") {
return;
}
// get api object for current dt table // Get api object for current dt table
var api = new $.fn.dataTable.Api(settings); const api = new $.fn.dataTable.Api(settings);
// get id of current table // Get id of current table
var id = api.table().node().id; const id = api.table().node().id;
// get initialization object for current table to retrieve custom settings // Get the initialization object for current table to retrieve custom settings
var initObj = api.init(); const initObj = api.init();
// only proceed if filter has been defined in current table, otherwise don't do anything. // Only proceed if a filter has been defined in the current table, otherwise don't do anything.
if (!("filterDropDown" in initObj)) return; if (!("filterDropDown" in initObj)) {
return;
}
// get current filter definition // Get current filter definition
var filterDef = parseInitArray(initObj.filterDropDown); const filterDef = parseInitArray(initObj.filterDropDown);
if (filterDef.ajax == null) { if (filterDef.ajax == null) {
api.columns(filterDef.columnsIdxList).every(function () { api.columns(filterDef.columnsIdxList).every(function () {
let column = this const column = this;
let select = initSelectForColumn(id, column); const select = initSelectForColumn(id, column);
column.data().unique().sort().each(function (d) {
addOption(select, d) column
.data()
.unique()
.sort()
.each((d) => {
addOption(select, d);
}); });
}); });
} else { } else {
// fetch column options from server for server side processing // Fetch column options from server for server side processing
let columnsQuery = ( const columnsQuery = `columns=${encodeURIComponent(
"columns="
+ encodeURIComponent(
api.columns(filterDef.columnsIdxList).dataSrc().join() api.columns(filterDef.columnsIdxList).dataSrc().join()
) )}`;
)
$.getJSON(filterDef.ajax + "?" + columnsQuery, function (columnsOptions) { $.getJSON(`${filterDef.ajax}?${columnsQuery}`, (columnsOptions) => {
api.columns(filterDef.columnsIdxList).every(function () { api.columns(filterDef.columnsIdxList).every(function () {
let column = this; const column = this;
let select = initSelectForColumn(id, column); const select = initSelectForColumn(id, column);
let columnName = column.dataSrc() const columnName = column.dataSrc();
if (columnName in columnsOptions) { if (columnName in columnsOptions) {
columnsOptions[columnName].forEach(function (d) { columnsOptions[columnName].forEach((d) => {
addOption(select, d) addOption(select, d);
}); });
} else { } else {
console.warn( console.warn(
"Missing column '" + columnName + "' in ajax response." `Missing column '${columnName}' in ajax response.`
) );
} }
}); });
}); });
} }
}); });
})(jQuery);
}(jQuery));

View File

@ -1 +1 @@
!function(t){function n(t){let n={columns:[],columnsIdxList:[],bootstrap:!1,autoSize:!0,ajax:null,label:"Filter "};return"bootstrap"in t&&"boolean"==typeof t.bootstrap&&(n.bootstrap=t.bootstrap),"autoSize"in t&&"boolean"==typeof t.autoSize&&(n.autoSize=t.autoSize),"ajax"in t&&"string"==typeof t.ajax&&(n.ajax=t.ajax),"label"in t&&"string"==typeof t.label&&(n.label=t.label),"columns"in t&&t.columns.forEach(function(t){if("idx"in t&&"number"==typeof t.idx){let e=t.idx;n.columns[e]={title:null,maxWidth:null,autoSize:!0},n.columnsIdxList.push(e),"title"in t&&"string"==typeof t.title&&(n.columns[e].title=t.title),"maxWidth"in t&&"string"==typeof t.maxWidth&&(n.columns[e].maxWidth=t.maxWidth),"autoSize"in t&&"boolean"==typeof t.autoSize&&(n.columns[e].autoSize=t.autoSize)}}),n}function e(t,n){""!=n&&t.append('<option value="'+n+'">'+n+"</option>")}function i(n,e){let i=t("#"+n+"_filterSelect"+e.index());return i.on("change",function(){let n=t.fn.dataTable.util.escapeRegex(t(this).val());e.search(n?"^"+n+"$":"",!0,!1).draw()}),i}t(document).on("preInit.dt",function(e,i){if("dt"===e.namespace){var o=new t.fn.dataTable.Api(i),l=o.table().node().id,a=o.init();if("filterDropDown"in a){var u=n(a.filterDropDown);if(0!=u.columns.length){var s=o.table().container(),c=l+"_filterWrapper",r=c+" "+(u.bootstrap?"form-inline":"");t(s).prepend('<div id="'+c+'" class="'+r+'">'+u.label+"</div>"),o.columns(u.columnsIdxList).every(function(){let n=this.index(),e=null!==u.columns[n].title?u.columns[n].title:t(this.header()).html();""==e&&(e="column "+(n+1));let i=l+"_filterSelect"+n;t("#"+c).append('<select id="'+i+'" class="form-control '+l+'_filterSelect"></select>');let o=t("#"+i).empty().append('<option value="">('+e+")</option>");u.autoSize&&u.columns[n].autoSize&&screen.width>768&&o.css("max-width",o.outerWidth()),null!==u.columns[n].maxWidth&&o.css("max-width",u.columns[n].maxWidth)})}}}}),t(document).on("init.dt",function(o,l){if("dt"===o.namespace){var a=new t.fn.dataTable.Api(l),u=a.table().node().id,s=a.init();if("filterDropDown"in s){var c=n(s.filterDropDown);if(null==c.ajax)a.columns(c.columnsIdxList).every(function(){let t=i(u,this);this.data().unique().sort().each(function(n){e(t,n)})});else{let n="columns="+encodeURIComponent(a.columns(c.columnsIdxList).dataSrc().join());t.getJSON(c.ajax+"?"+n,function(t){a.columns(c.columnsIdxList).every(function(){let n=i(u,this),o=this.dataSrc();o in t?t[o].forEach(function(t){e(n,t)}):console.warn("Missing column '"+o+"' in ajax response.")})})}}}})}(jQuery); ($=>{"use strict";const parseInitArray=initArray=>{const filterDef={columns:[],columnsIdxList:[],bootstrap:false,bootstrap_version:3,autoSize:true,ajax:null,label:"Filter "};if("bootstrap"in initArray&&typeof initArray.bootstrap==="boolean"){filterDef.bootstrap=initArray.bootstrap}if("bootstrap_version"in initArray&&typeof initArray.bootstrap_version==="number"){filterDef.bootstrap_version=initArray.bootstrap_version}if("autoSize"in initArray&&typeof initArray.autoSize==="boolean"){filterDef.autoSize=initArray.autoSize}if("ajax"in initArray&&typeof initArray.ajax==="string"){filterDef.ajax=initArray.ajax}if("label"in initArray&&typeof initArray.label==="string"){filterDef.label=initArray.label}if("columns"in initArray){initArray.columns.forEach(initColumn=>{if("idx"in initColumn&&typeof initColumn.idx==="number"){const idx=initColumn.idx;filterDef.columns[idx]={title:null,maxWidth:null,autoSize:true};filterDef.columnsIdxList.push(idx);if("title"in initColumn&&typeof initColumn.title==="string"){filterDef.columns[idx].title=initColumn.title}if("maxWidth"in initColumn&&typeof initColumn.maxWidth==="string"){filterDef.columns[idx].maxWidth=initColumn.maxWidth}if("autoSize"in initColumn&&typeof initColumn.autoSize==="boolean"){filterDef.columns[idx].autoSize=initColumn.autoSize}}})}return filterDef};const addOption=(select,d)=>{if(d!==""){select.append(`<option value="${d}">${d}</option>`)}};const initSelectForColumn=(id,column)=>{const select=$(`#${id}_filterSelect${column.index()}`);$(select).change(()=>{const val=$.fn.dataTable.util.escapeRegex($(select).val());column.search(val?`^${val}$`:"",true,false).draw()});return select};$(document).on("preInit.dt",(e,settings)=>{if(e.namespace!=="dt"){return}const api=new $.fn.dataTable.Api(settings);const id=api.table().node().id;const initObj=api.init();if(!("filterDropDown"in initObj)){return}const filterDef=parseInitArray(initObj.filterDropDown);if(filterDef.columns.length===0){return}const container=api.table().container();const filterWrapperId=`${id}_filterWrapper`;let divCssClass=`${filterWrapperId} ${filterDef.bootstrap?"form-inline":""}`;if(filterDef.bootstrap&&filterDef.bootstrap_version===5){divCssClass=`${filterWrapperId} input-group my-3`}$(container).prepend(`<div id="${filterWrapperId}" class="${divCssClass}"><span class="pt-2">${filterDef.label}</span></div>`);api.columns(filterDef.columnsIdxList).every(function(){const idx=this.index();let colName=filterDef.columns[idx].title!==null?filterDef.columns[idx].title:$(this.header()).html();if(colName===""){colName=`column ${idx+1}`}const selectId=`${id}_filterSelect${idx}`;let selectMarkup=`<select id="${selectId}" class="form-control ${id}_filterSelect"></select>`;if(filterDef.bootstrap&&filterDef.bootstrap_version===5){selectMarkup=`<select id="${selectId}" class="form-select w-auto ms-2 ${id}_filterSelect"></select>`}$("#"+filterWrapperId).append(selectMarkup);const select=$("#"+selectId).empty().append(`<option value="">(${colName})</option>`);if(filterDef.autoSize&&filterDef.columns[idx].autoSize&&screen.width>768){select.css("max-width",select.outerWidth())}if(filterDef.columns[idx].maxWidth!==null){select.css("max-width",filterDef.columns[idx].maxWidth)}})});$(document).on("init.dt",(e,settings)=>{if(e.namespace!=="dt"){return}const api=new $.fn.dataTable.Api(settings);const id=api.table().node().id;const initObj=api.init();if(!("filterDropDown"in initObj)){return}const filterDef=parseInitArray(initObj.filterDropDown);if(filterDef.ajax==null){api.columns(filterDef.columnsIdxList).every(function(){const column=this;const select=initSelectForColumn(id,column);column.data().unique().sort().each(d=>{addOption(select,d)})})}else{const columnsQuery=`columns=${encodeURIComponent(api.columns(filterDef.columnsIdxList).dataSrc().join())}`;$.getJSON(`${filterDef.ajax}?${columnsQuery}`,columnsOptions=>{api.columns(filterDef.columnsIdxList).every(function(){const column=this;const select=initSelectForColumn(id,column);const columnName=column.dataSrc();if(columnName in columnsOptions){columnsOptions[columnName].forEach(d=>{addOption(select,d)})}else{console.warn(`Missing column '${columnName}' in ajax response.`)}})})}})})(jQuery);

View File

@ -0,0 +1,37 @@
{% load i18n %}
<div id="esi-alert" class="col-12 align-self-stretch py-2 collapse">
<div class="alert alert-warning">
<p class="text-center ">{% translate 'Your Server received an ESI error response code of ' %}<b id="esi-code">?</b></p>
<hr>
<pre id="esi-data" class="text-center text-wrap"></pre>
</div>
</div>
<script>
const elemCard = document.getElementById("esi-alert");
const elemMessage = document.getElementById("esi-data");
const elemCode = document.getElementById("esi-code");
fetch('{% url "authentication:esi_check" %}')
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Something went wrong");
})
.then((responseJson) => {
console.log("ESI Check: ", JSON.stringify(responseJson, null, 2));
const status = responseJson.status;
if (status != 200) {
elemCode.textContent = status
elemMessage.textContent = responseJson.data.error;
new bootstrap.Collapse(elemCard, {
toggle: true
})
}
})
.catch((error) => {
console.log(error);
});
</script>

View File

@ -2,7 +2,7 @@
{% load humanize %} {% load humanize %}
{% if notifications %} {% if notifications %}
<div class="col-12 align-self-stretch pb-2"> <div id="aa-dashboard-panel-admin-notifications" class="col-12 align-self-stretch pb-2">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@ -51,10 +51,10 @@
</div> </div>
{% endif %} {% endif %}
<div class="col-12 align-self-stretch py-2"> <div class="col-12 align-self-stretch pb-2">
<div class="card"> <div class="card">
<div class="card-body d-flex flex-row flex-wrap"> <div class="card-body d-flex flex-row flex-wrap">
<div class="col-xl-6 col-lg-12 col-md-12 col-sm-12"> <div id="aa-dashboard-panel-software-version" class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
<h4 class="ms-auto me-auto text-center"> <h4 class="ms-auto me-auto text-center">
{% translate "Software Version" %} {% translate "Software Version" %}
</h4> </h4>
@ -97,7 +97,7 @@
</div> </div>
</div> </div>
<div class="col-xl-6 col-lg-12 col-md-12 col-sm-12"> <div id="aa-dashboard-panel-task-queue" class="col-xl-6 col-lg-12 col-md-12 col-sm-12">
<h4 class="ms-auto me-auto text-center"> <h4 class="ms-auto me-auto text-center">
{% translate "Task Queue" %} {% translate "Task Queue" %}
</h4> </h4>

View File

@ -34,12 +34,6 @@
padding-top: {% header_padding_size %} !important; padding-top: {% header_padding_size %} !important;
} }
{% endif %} {% endif %}
.auth-logo {
background-position: bottom;
background-repeat: no-repeat;
background-image: url("{% static 'allianceauth/images/auth-logo.png' %}") ;
}
</style> </style>
{% block extra_css %}{% endblock extra_css %} {% block extra_css %}{% endblock extra_css %}
</head> </head>
@ -59,20 +53,18 @@
{% block header_nav_brand %}{{ SITE_NAME }}{% endblock %} {% block header_nav_brand %}{{ SITE_NAME }}{% endblock %}
</div> </div>
<div class="collapse navbar-collapse" id="navbarexpand"> <div class="collapse navbar-collapse ms-2 px-2" id="navbarexpand">
<div class="m-2"></div> <ul id="nav-left" class="nav navbar-nav me-auto">
<ul id="nav-left" class="navbar-nav nav me-auto">
{% block header_nav_collapse_left %} {% block header_nav_collapse_left %}
{% endblock %} {% endblock %}
</ul> </ul>
<ul id="nav-right" class="navbar-nav"> <ul id="nav-right" class="nav navbar-nav">
{% block header_nav_collapse_right %} {% block header_nav_collapse_right %}
{% endblock %} {% endblock %}
</ul> </ul>
<ul id="nav-right-character-control" class="navbar-nav"> <ul id="nav-right-character-control" class="nav navbar-nav">
{% block header_nav_user_character_control %} <!-- Default to add char and swap main --> {% block header_nav_user_character_control %} <!-- Default to add char and swap main -->
{% include 'allianceauth/top-menu-rh-default.html' %} {% include 'allianceauth/top-menu-rh-default.html' %}
{% endblock %} {% endblock %}

View File

@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
{% load navactive %} {% load navactive %}
{% load menu_menu_items %} {% load menu_items %}
<div class="col-sm-2 auth-side-navbar" role="navigation"> <div class="col-sm-2 auth-side-navbar" role="navigation">
<div class="collapse navbar-collapse auth-menus-collapse auth-side-navbar-collapse"> <div class="collapse navbar-collapse auth-menus-collapse auth-side-navbar-collapse">

View File

@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
<li class="nav-item active"> <li class="nav-item">
<a href="{% url 'authentication:add_character' %}" class="nav-link" title="{% translate 'Add Character' %}"> <a href="{% url 'authentication:add_character' %}" class="nav-link" title="{% translate 'Add Character' %}">
<i class="fa-solid fa-plus"></i> <i class="fa-solid fa-plus"></i>
<span class="d-lg-none d-md-inline m-2">{% translate "Add Character" %}</span> <span class="d-lg-none d-md-inline m-2">{% translate "Add Character" %}</span>

View File

@ -0,0 +1,3 @@
{% load static %}
<img src="{% static 'allianceauth/images/auth-logo.svg' %}" width="{{ logo_width|default:"128px" }}" height="{% if logo_height %}{{ logo_ }}{% else %}{{ logo_width|default:"128px" }}{% endif %}" alt="{{ SITE_NAME }}">

View File

@ -2,6 +2,20 @@ from allianceauth import hooks
from allianceauth.theme.hooks import ThemeHook from allianceauth.theme.hooks import ThemeHook
CSS_STATICS = [{
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css",
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
}]
JS_STATICS = [{
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
}, {
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
}]
class BootstrapThemeHook(ThemeHook): class BootstrapThemeHook(ThemeHook):
""" """
Bootstrap in all its glory! Bootstrap in all its glory!
@ -13,21 +27,35 @@ class BootstrapThemeHook(ThemeHook):
self, self,
"Bootstrap", "Bootstrap",
"Powerful, extensible, and feature-packed frontend toolkit.", "Powerful, extensible, and feature-packed frontend toolkit.",
css=[{ css=CSS_STATICS,
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css", js=JS_STATICS,
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg==" header_padding="3.5em"
}], )
js=[{
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ==" class BootstrapDarkThemeHook(ThemeHook):
}, { """
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js", Bootstrap in all its glory!, but _dark_
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg==" https://getbootstrap.com/
}], """
def __init__(self):
ThemeHook.__init__(
self,
"Bootstrap Dark",
"Powerful, extensible, and feature-packed frontend toolkit.",
css=CSS_STATICS,
js=JS_STATICS,
html_tags="data-bs-theme=dark",
header_padding="3.5em" header_padding="3.5em"
) )
@hooks.register('theme_hook')
def register_bootstrap_dark_hook():
return BootstrapDarkThemeHook()
@hooks.register('theme_hook') @hooks.register('theme_hook')
def register_bootstrap_hook(): def register_bootstrap_hook():
return BootstrapThemeHook() return BootstrapThemeHook()

View File

@ -1,11 +0,0 @@
from django.apps import AppConfig
class BootstrapDarkThemeConfig(AppConfig):
name = "allianceauth.theme.bootstrap_dark"
label = "bootstrap_dark"
version = "5.3.0"
verbose_name = f"Bootstrap Dark v{version}"
def ready(self):
pass

View File

@ -1,34 +0,0 @@
from allianceauth import hooks
from allianceauth.theme.hooks import ThemeHook
class BootstrapDarkThemeHook(ThemeHook):
"""
Bootstrap in all its glory!, but _dark_
https://getbootstrap.com/
"""
def __init__(self):
ThemeHook.__init__(
self,
"Bootstrap Dark",
"Powerful, extensible, and feature-packed frontend toolkit.",
css=[{
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css",
"integrity": "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
}],
js=[{
"url": "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
"integrity": "sha512-TPh2Oxlg1zp+kz3nFA0C5vVC6leG/6mm1z9+mA81MI5eaUVqasPLO8Cuk4gMF4gUfP5etR73rgU/8PNMsSesoQ=="
}, {
"url": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js",
"integrity": "sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
}],
html_tags="data-bs-theme=dark",
header_padding="3.5em"
)
@hooks.register('theme_hook')
def register_bootstrap_dark_hook():
return BootstrapDarkThemeHook()

Some files were not shown because too many files have changed in this diff Show More